将布尔结果转换为数字/整数

441

我有一个变量,存储的是 falsetrue,但我需要分别得到 01。如何做到这一点?


9
以下是一些提供的技术性能比较:http://jsperf.com/conversion-from-boolean-to-number。 - Sam
6
Node.JS 用户应该使用 bool === true ? 1 : 0,因为它在 V8 引擎中是最快的。 - Qix - MONICA WAS MISTREATED
6
或者只需使用 bool ? 1 : 0;。该语句的意思是,如果布尔值为真,则返回1,否则返回0。 - Mouloud85
22个回答

690

使用一元加号运算符,它将其操作数转换为数字。

+ true; // 1
+ false; // 0

请注意,当然您仍需要在服务器端对数据进行清理处理,因为无论客户端代码如何,用户都可以向您的服务器发送任何数据。


66
虽然很酷(我之前从未考虑过这个),但它非常慢(确切地说,在Chrome中慢了97%)。要小心! - Qix - MONICA WAS MISTREATED
6
看看这个修改版Number() 的速度更慢了。 - Qix - MONICA WAS MISTREATED
41
似乎 bool === true ? 1 : 0 是最快的,其次是 bool | 0 - Qix - MONICA WAS MISTREATED
1
乘法(例如3 * false)感觉很奇怪,但它确实有效。 :) 谢谢! - mattsoave
4
在TypeScript中不允许某些事情,并不意味着你在JavaScript中就不能这么做。它们是不同的语言(尽管相关)。 - lonesomeday
显示剩余10条评论

490

Javascript有一个三元运算符可以使用:

var i = result ? 1 : 0;

15
为什么?这种方法基于“真实性(truthiness)”,它更加通用,可以接受任何类型的输入(字符串、数字等)。虽然一元运算符的方法非常巧妙,但如果我传入一个字符串,它会返回“NaN”。因此,如果您需要保证输入为 L33T,并且确信输入是正确的,就使用一元运算符方法;否则,我认为三元运算符和真值测试方法最佳。 - gdibble
这个解决方案基本上是使用三元运算符来最小化一个 if 语句。 - Mr PizzaGuy
3
三元运算符是其中一种速度最快的方式。其他解决方案,如+trueNumber(true)非常慢。请参见基准测试结果 - Domske
@Dominik 那个基准测试工具非常令人困惑和误导。https://jsbench.me/ 是一个更好的选择。 - m4heshd

150

我认为最好的解决方案是:

fooBar | 0

这在 asm.js 中用于强制整数类型。


3
其中之一最快;+1。 - Qix - MONICA WAS MISTREATED
3
好的,你可以使用"Boolean ^ 0"。或者XOR都可以。 - Artfaith
3
如果 fooBar 不是一个整数,那么这个操作不会返回数字 1,对吗? - ESR
@ESR 它将所有内容转换为数字,但不总是您想要的数字,如果您正在处理其他真实类型。 1 | 0 = 1; 0 | 0 = 0; true | 0 = 1; false | 0 = 0; 'foo' | 0 = 0; undefined | 0 = 0 - Luke Miles
3
Typescript 错误:算术操作的左侧必须是类型为 'any'、'number'、'bigint' 或枚举类型。 - Cedric Ipkiss
fooBar和1是为了完整性而存在的。 - Denis Giffeler

99

我更喜欢使用Number函数。它可以将一个对象转换为数字。

例如:

var myFalseBool = false;
var myTrueBool = true;

var myFalseInt = Number(myFalseBool);
console.log(myFalseInt === 0);

var myTrueInt = Number(myTrueBool);
console.log(myTrueInt === 1);
你可以在jsFiddle中测试它。

5
到目前为止,这是最好的答案。当然,只有“it takes an object”不正确。 - Rudie
3
MDN的链接比W3Schools更好(哎呀!): https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number - Olivvv
4
我认为这是最好的方法,因为它易于阅读且能准确表达意图。 - Sam
6
它也是最慢的。 - Qix - MONICA WAS MISTREATED
三元解决方案是最快的方法之一。其他解决方案,如 +trueNumber(true) 非常慢。请参见基准测试 - Domske
显示剩余2条评论

60

这个任务的正式操作方式如下:

Number(true) // 1
Number(false) // 0

2
终于有一些像样的答案了。谢谢。 - Erik Campobadal
三元解决方案是最快的方法之一。其他解决方案,如 +trueNumber(true) 非常慢。请参见基准测试 - Domske
在某些情况下,这个不起作用会产生NaN。 - Devmyselz
@Devmyselz,你能给一个产生 NaN 的例子吗? - Philip
@Philip Number("True"); - Devmyselz
1
@Devmyselz 问题是将布尔值转换为数字。"True" 不是布尔值。 - rooby

52

我创建了一个JSperf比较所有建议答案的测试。

简单来说 - 所有现代浏览器的最佳选项是:

val | 0;

更新:

看起来这些方法现在都差不多,除了Number()函数最慢,而最好的是val === true ? 1 : 0;


2
有趣的是,在macOS 10.13.3上的Chrome 64.0.3282中,三元运算符现在是最快的。 - 2540625
1
那将是当时最快的选择,但这与它是最佳选择不同。 - mikemaccana

35

4
有趣。今天我学到了新东西。不过,我不会在任何项目中使用这种技术,因为它可能会让未来的我或团队成员感到困惑。 - nicholaides
2
hacky js 是我最喜欢的。说真的,+1。 - Todd

32

简而言之:避免使用Number构造函数和+bool,默认使用简单的if语句;如果您的项目中基准测试表现更好,则可以使用bool | 01 * bool

这是一个相当古老的问题,有许多有效的答案。我注意到这里的所有基准测试都不相关 - 没有考虑分支 预测。此外,现在的JS引擎不仅仅解释代码,它们JIT 编译成本机代码并在执行前进行优化。这意味着除了分支预测外,编译器甚至可以用最终值替换表达式。

现在,这两个因素如何影响布尔转整数的性能呢?让我们来看看!在我们进入基准测试之前,了解我们要测试什么非常重要。对于转换,我们使用以下七种转换方法:数字构造函数:Number(bool)、if语句(使用三目运算符):bool ? 1 : 0、一元操作符++bool、按位或:bool | 0、按位与:bool & 1、按位双重否定:~~bool、数字乘法:bool * 1 “Conversion”指将false转换为0,将true转换为11。每个转换方法都运行了100,000次,测量每毫秒的操作次数。在下表中,转换方法将根据其结果分组。结果后面的百分比表示该方法相对于同一浏览器中最快方法慢的程度。如果没有百分比,则该方法要么是最快的,要么差异可以忽略不计(小于0.01%)。基准是在配备Apple M1 Pro 10核CPU和16GB RAM的Macbook Pro 16英寸机器上进行的。浏览器是Chrome 102、Firefox 101和Safari 15.5。
第一个benchmark将常量true转换。
方法 Chrome (V8) Firefox (Spidermonkey) Safari (Webkit)
Number(bool) 31745.89 392.35 - 91.48% 31231.79
bool ? 1 : 0 31592.8 - 0.48% 4602.64 27533.47 - 11.84%
+bool 31332.57 - 1.3% 4463.41 - 3.02% 27378.7 - 12.34%
bool | 0 31488.5 - 0.81% 4441.4 - 3.5% 27222 - 12.84%
bool & 1 31383.17 - 1.14% 4459.64 - 3.11% 27317.41 - 12.53%
~~bool 31265.85 - 1.51% 4442.35 - 3.48% 27434.72 - 12.16%
bool * 1 31374.4 - 1.17% 4444.05 - 3.45% 27381.19 - 12.33%
有趣!V8显示了一些巨大的数字,它们都大致相同!Spidermonkey并没有真正闪耀,但我们可以看到按位与乘法技巧排在第一位,三元if排在第二位。最后,Webkit的Number与V8的表现类似,其他方法落后,但彼此之间非常接近。有哪些收获?浏览器大多数情况下能够用简单的值1替换我们的转换。当我们能够将布尔值心理上替换为常量值时,就会进行此优化。Number构造函数是一个有趣的异常情况 - 它在Firefox中严重落后(慢了91%!),而在Safari中则是最快的!
上述情况不是我们在实际项目中遇到的情况。因此,让我们更改变量:bool现在为Math.random() < 0.5。这将产生50%的true和50%的false的机会。我们的结果会发生变化吗?让我们运行benchmark来查看。
方法 Chrome (V8) Firefox (Spidermonkey) Safari (Webkit)
Number(bool) 1648.83 - 2.26% 280.34 - 86.4% 8014.69
bool ? 1 : 0 804.27 - 52.32% 731.57 - 64.5% 1294.02 - 83.85%
+bool 1670.79 - 0.95% 2057.94 7753.99 - 3.25%
bool | 0 1668.22 - 1.11% 2054.17 7764.81 - 3.12%
bool & 1 1675.52 - 0.67% 2056.76 7193.08 - 10.25%
~~bool 1676.24 - 0.63% 2056.18 7669.48 - 4.31%
bool * 1 1686.88 2060.88 7751.48 - 3.28%
现在结果更加一致。我们在各种浏览器中看到三元运算符、位运算和乘法方法的相似数字,而Number构造函数在Firefox上表现最差。由于生成分支,三元运算符落后了。总体而言,Safari似乎是我们的最佳表现者,每种方法都产生了极快的结果!
现在让我们看看分支预测如何影响我们的结果,使用以下benchmark,我们将布尔变量更改为Math.random() < 0.01,这意味着1%的true,99%的false
方法 Chrome(V8) Firefox(Spidermonkey) Safari(Webkit)
Number(bool) 1643.13 - 1.68% 280.06 - 86.4% 8071.65
bool ? 1 : 0 1590.55 - 4.83% 1970.66 - 4.32% 7119.59 - 11.8%
+bool 1662.09 - 0.55% 2054.09 7762.03 - 3.84%
bool | 0 1669.35 2051.85 7743.95 - 4.06%
bool & 1 1661.09 - 0.61% 2057.62 7454.45 - 7.65%
~~bool 1662.94 - 0.5% 2059.65 7739.4 - 4.12%
bool * 1 1671.28 2048.21 7787.38 - 3.52%
意外的?预期的?我会说是后者,因为在这种情况下,由于三元if和位操作之间的差异较小,分支预测在几乎所有情况下都成功了。所有其他结果都相同,在这里没有什么可说的了。我仍然会指出Number在Firefox中的可怕性能-为什么?
这个努力让我们回到最初的问题:如何在Javascript中将布尔值转换为整数?以下是我的建议:
  • 一般情况下,请使用 if 语句。不要过于聪明,通常情况下浏览器会做得更好,而且这通常是大多数情况。它们是所有方法中最易读和最清晰的。顺便说一下,为了可读性,也许可以使用 if (bool) 而不是那个丑陋的三元运算符!我希望 JavaScript 有像 RustPython 那样的东西...
  • 只有在真正需要时才使用 rest。也许在您的项目中进行基准测试时表现不佳,并且您发现一个讨厌的 if 导致性能不佳 - 如果是这种情况,请随意进入 branchless programming!但不要太深入这个兔子洞,相信我,没有人会从像 -1 * (a < b) + 1 * (a > b) 这样的东西中受益。

以下是一些具体细节:

  • 避免使用Number(bool)。虽然全球范围内,Chromium平台(Chrome+Edge)的市场份额约为68%,Safari为19%,Firefox仅为3.6%,但有足够快速且不会完全牺牲用户的其他方法。Firefox在桌面市场份额中占7%,这相当于173百万用户。
  • 在旧的基准测试中,+bool在Firefox中表现得与Number类似糟糕,也许也要考虑到这一点-位操作技巧和乘法在所有浏览器和所有情况下都能给出一致的高性能结果。bool | 0最有可能为其他开发人员所熟悉。

如果您一直阅读到了最后,我将永远感激您-这是我第一个更长、更重要的StackOverflow答案,如果它有所帮助并且具有洞察力,那对我来说意义重大。如果您发现任何错误,请随时纠正我!

编辑:之前的基准测试工具提供了含糊不清、没有度量单位的结果。我已经更改了它,并添加了Safari的基准测试,这些测试影响了结论。
  1. 定义转换是因为布尔值转整数的含义并不十分清晰。例如,Go 根本不支持这种转换

3
点赞你的努力,但我想不出这种操作性能真正有用的情况 :) - skwisgaar
@skwisgaar 完全同意,尽管我认为这个操作大多数情况下是在人们感觉需要优化时使用的。 - Teodor Maxim
1
很棒的文章,但我正试图弄清楚基准测试数字的解释,因为你指出“每秒操作次数”。在我的五年旧的Acer笔记本电脑的V8浏览器调试器中,以下一百万个操作在36毫秒内运行完成:start = performance.now(); for (let i = 0; i < 1000000; i++) { let x = + (Math.random() <0.5);} end = performance.now(); console.log(end-start)。我对此的理解有误吗? - Trentium
谢谢您的回复!我自己运行基准测试后,数字看起来更像是每毫秒操作数。虽然在Chrome和Firefox的DevTools中,相同的基准测试似乎也在我的计算机上运行得更快 - 也许JSBench是一个瓶颈。我现在会更改测量单位,但这必须进一步研究。 - Teodor Maxim
@TeodorMaxim 刚看到你的回复...听起来 JSBench 报告的内容不太清楚...你最好创建自己的小型基准测试工具,这样你就可以确切地知道正在测量什么,同时也确切地知道如何解释结果。 - Trentium
谁会关心那些人类无法察觉的微小性能提升呢? - Angel

21

当JavaScript期望一个数字值但实际接收到一个布尔值时,它会将该布尔值转换为数字:true和false分别转换为1和0。因此,您可以利用这一点;

var t = true;
var f = false;

console.log(t*1); // t*1 === 1
console.log(f*1); // f*1 === 0 

console.log(+t); // 0+t === 1 or shortened to +t === 1
console.log(+f); //0+f === 0 or shortened to +f === 0

更多阅读:类型转换。请参考《JavaScript权威指南》第3.8章。


在某些情况下会产生 NaN - Devmyselz

15

我最近写代码时也遇到了这个问题。我的解决方案是使用位与运算符。

var j = bool & 1;

解决经常出现的问题更快捷的方法是创建一个函数。这样可以提高代码的可读性,方便在维护阶段进行理解,并且可以消除书写错误的可能性。

function toInt( val ) {
    return val & 1;
}

var j = toInt(bool);

编辑 - 2014年9月10日

在Chrome浏览器中,使用三元运算符和相等操作符进行转换并不能提高速度。这种情况似乎没有任何意义,但我想这可能是某种低级别优化,从某些方面来看是有意义的。

var j = boolValue === true ? 1 : 0;

自己测试一下:http://jsperf.com/boolean-int-conversion/2

在 Firefox 和 Internet Explorer 中,通常使用我发布的版本速度更快。

编辑-2017年7月14日

好吧,我不会告诉你应该使用哪个版本。每个浏览器都在不断变化,每种方法执行操作的速度也在不断变化。有一段时间,Chrome实际上比其他浏览器的按位&版本表现更好,但突然就变得更糟了。我不知道他们在做什么,所以我就只是说谁在乎呢。几乎没有理由关心这种操作完成的速度。即使在移动设备上,这也是一个微不足道的操作。

另外,这里有一种新的添加“toInt”原型的方法,它不能被覆盖。

Object.defineProperty(Boolean.prototype, "toInt", { value: function()
{
    return this & 1;
}});

我这篇帖子已经被踩了两次。为什么不解释一下你为什么要踩它呢?否则,这只是一个没有理由的踩。 - Nicholas R. Grant
1
99次jsperf的结果只会让你走上过早优化的道路,而你应该把注意力放在那个丑陋的SQL语句上,而不是微调循环中的纳秒。感谢提供几种不同的方法来解决这个问题。 - RozzA
什么SQL语句?这里没有一个查询。如果你指的是JSPerf,那是我从别人的测试中链接过来的。这不是我的。我真的不关心这个性能方面,因为它是一个无意义的操作。我创建了自己的语言,几乎与JS的功能相同,我记得将其转换为int是一个非常快的操作。爬行原型链则不是。这就是为什么我仍然建议我最初的方法,使用一个简单的函数,可以由编译器内联。 - Nicholas R. Grant
SQL这个东西是一个概括。感谢您的见解。 - RozzA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接