我有一个存储false或true的变量,但我需要分别为0或1。我该怎么做呢?


当前回答

您可以通过简单地扩展布尔原型来做到这一点

Boolean.prototype.intval = function(){return ~~this}

不太容易理解那里发生了什么所以另一个版本会是

Boolean.prototype.intval = function(){return (this == true)?1:0}

你可以做一些事情

document.write(true.intval());

当我使用布尔值存储条件时,我经常将它们转换为位域,在这种情况下,我最终使用了原型函数的扩展版本

Boolean.prototype.intval = function(places)
{
 places = ('undefined' == typeof(places))?0:places; 
 return (~~this) << places
}

你可以这样做

document.write(true.intval(2))

它的输出是4。

其他回答

+ ! !允许你对一个变量应用这个,即使它是未定义的:

+!!undefined    // 0
+!!false        // 0
+!!true         // 1

+!!(<boolean expression>)  // 1 if it evaluates to true, 0 otherwise

TL;DR:避免数字构造函数和+bool;默认情况下使用简单的if;如果项目中的基准测试这样做更好,请使用bool | 0,1 * bool。

这是一个相当老的问题,有很多有效的答案。我注意到这里所有的基准测试都是不相关的——没有一个考虑到分支预测。而且,现在JS引擎不只是简单地解释代码,而是将其JIT编译为本机机器码,并在执行之前对其进行优化。这意味着,除了分支预测,编译器甚至可以用表达式的最终值替换表达式。

那么,这两个因素是如何影响布尔到整数转换的性能的呢?让我们一探究竟!在开始基准测试之前,了解基准测试的对象是很重要的。对于转换,我们使用以下7种转换方法:

数字构造函数:Number(bool) If语句(使用三元):bool ?1:0 一元运算符+:+bool 按位OR: bool | 0 按位AND: bool & 1 位双NOT: ~~bool 数字乘法:bool * 1

"Conversion" means converting false to 0 and true to 11. Each conversion method is ran 100000 times, measuring operations/millisecond. In the following tables, conversion methods will be grouped to their results accordingly. The percentage after the result represents how slow this method is compared to the fastest, in the same browser. If there is no percentage, the method is either the fastest or the difference is negligible (<0.01%). Benchmarks are run on a Macbook Pro 16-inch machine, with the Apple M1 Pro 10-core CPU and 16GB of RAM. Browsers are Chrome 102, Firefox 101 and Safari 15.5.

第一个基准测试转换常量true:

Method 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%

Interesting! V8 shows some huge numbers, all of them approximately the same! Spidermonkey doesn't really shine, but we can see that the bitwise and multiplication tricks come first, and the ternary if second. Finally, Webkit's Number does similarly to V8's and the other methods fall behind, but are all close to each other. What are the takeaways? Browsers mostly manage to replace our conversions with simply the value 1. This optimization will take place where we can mentally replace the boolean to a constant value. The Number constructor is an intriguing anomaly - it severly falls behind in Firefox (91% slower!), while in Safari it is the fastest!

上述情况在实际项目中是不会遇到的。所以让我们改变变量:bool值现在是Math.random() < 0.5。这就产生了50%的真概率,50%的假概率。我们的结果会改变吗?让我们运行这个基准测试来看看。

Method 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%

现在的结果更加一致了。我们可以在不同浏览器中看到类似的三元if、位和乘法方法,并且Number构造函数在Firefox上的性能最差。三元在生成分支时落在后面。Safari似乎是我们整体表现最好的,每种方法都能产生极快的结果!

现在让我们看看分支预测如何影响下面基准测试的结果,其中我们将布尔变量改为Math.random() < 0.01,这意味着1%为真,99%为假。

Method 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和位hack之间的差异较小。其他的结果都是一样的,这里就不多说了。我还是要指出数字在Firefox中的糟糕表现——为什么?

这将我们带回到最初的问题:如何在Javascript中将bool类型转换为int类型?以下是我的建议:

Use if statements, in general. Don't get smart - the browser will do better, usually, and usually means most of the situations. They are the most readable and clear out of all the methods here. While we're at readability, maybe use if (bool) instead of that ugly ternary! I wish Javascript had what Rust or Python have... Use the rest when it's truly necessary. Maybe benchmarks in your project perform sub-standard, and you found that a nasty if causes bad performance - if that's the case, feel free to get into branchless programming! But don't go too deep in that rabbit hole, nobody will benefit from things like -1 * (a < b) + 1 * (a > b), believe me.

还有一些细节:

Avoid Number(bool). While it is true that the Chromium platform (Chrome + Edge) has about 68% market share globally, Safari 19% and Firefox a mere 3.6%, there are enough other fast-performing methods that won't fully sacrifice a percentage of your users. Firefox has 7% desktop market share, which amounts to a sizable number of 173 million users. In older benchmarks, +bool performed similarly bad to Number in Firefox, maybe take this in consideration, too - bitwise hacks and multiplication give consistently performant results across all browsers, in all situations. bool | 0 has the best chance to be familiar to other developers.

我将永远感谢你一直读到最后——这是我第一个更长的、重要的StackOverflow回答,如果它有帮助和深刻的见解,对我来说意味着世界。如果你发现任何错误,请随时纠正我!

编辑:之前的基准测试工具提供了模糊的结果,没有一个度量单位。我对它进行了更改,并为Safari添加了基准测试,这对结论产生了影响。


定义了转换,因为它并不真正清楚布尔到整数的含义。例如,Go根本不支持这种转换。

我已经测试了所有这些例子,我做了一个基准测试,最后我建议你选择较短的一个,它不会影响性能。

运行在Ubuntu服务器14.04,nodejs v8.12.0 - 26/10/18

    let i = 0;
console.time("TRUE test1")
    i=0;
    for(;i<100000000;i=i+1){
        true ? 1 : 0;
    }
console.timeEnd("TRUE test1")


console.time("FALSE test2")
    i=0;
    for(;i<100000000;i=i+1){
        false ? 1 : 0;
    }
console.timeEnd("FALSE test2")

console.log("----------------------------")

console.time("TRUE test1.1")
    i=0;
    for(;i<100000000;i=i+1){
        true === true ? 1 : 0;
    }
console.timeEnd("TRUE test1.1")


console.time("FALSE test2.1")
    i=0;
    for(;i<100000000;i=i+1){
        false === true ? 1 : 0;
    }
console.timeEnd("FALSE test2.1")

console.log("----------------------------")

console.time("TRUE test3")
    i=0;
    for(;i<100000000;i=i+1){
        true | 0;
    }
console.timeEnd("TRUE test3")

console.time("FALSE test4")
    i=0;
    for(;i<100000000;i=i+1){
        false | 0;
    }
console.timeEnd("FALSE test4")

console.log("----------------------------")

console.time("TRUE test5")
    i=0;
    for(;i<100000000;i=i+1){
        true * 1;
    }
console.timeEnd("TRUE test5")

console.time("FALSE test6")
    i=0;
    for(;i<100000000;i=i+1){
        false * 1;
    }
console.timeEnd("FALSE test6")

console.log("----------------------------")

console.time("TRUE test7")
    i=0;
    for(;i<100000000;i=i+1){
        true & 1;
    }
console.timeEnd("TRUE test7")

console.time("FALSE test8")
    i=0;
    for(;i<100000000;i=i+1){
        false & 1;
    }
console.timeEnd("FALSE test8")

console.log("----------------------------")

console.time("TRUE test9")
    i=0;
    for(;i<100000000;i=i+1){
        +true;
    }
console.timeEnd("TRUE test9")

console.time("FALSE test10")
    i=0;
    for(;i<100000000;i=i+1){
        +false;
    }
console.timeEnd("FALSE test10")

console.log("----------------------------")

console.time("TRUE test9.1")
    i=0;
    for(;i<100000000;i=i+1){
        0+true;
    }
console.timeEnd("TRUE test9.1")

console.time("FALSE test10.1")
    i=0;
    for(;i<100000000;i=i+1){
        0+false;
    }
console.timeEnd("FALSE test10.1")

console.log("----------------------------")

console.time("TRUE test9.2")
    i=0;
    for(;i<100000000;i=i+1){
        -true*-1;
    }
console.timeEnd("TRUE test9.2")

console.time("FALSE test10.2")
    i=0;
    for(;i<100000000;i=i+1){
        -false*-1;
    }
console.timeEnd("FALSE test10.2")

console.log("----------------------------")

console.time("TRUE test9.3")
    i=0;
    for(;i<100000000;i=i+1){
        true-0;
    }
console.timeEnd("TRUE test9.3")

console.time("FALSE test10.3")
    i=0;
    for(;i<100000000;i=i+1){
        false-0;
    }
console.timeEnd("FALSE test10.3")

console.log("----------------------------")

console.time("TRUE test11")
    i=0;
    for(;i<100000000;i=i+1){
        Number(true);
    }
console.timeEnd("TRUE test11")

console.time("FALSE test12")
    i=0;
    for(;i<100000000;i=i+1){
        Number(false);
    }
console.timeEnd("FALSE test12")

console.log("----------------------------")

console.time("TRUE test13")
    i=0;
    for(;i<100000000;i=i+1){
        true + 0;
    }
console.timeEnd("TRUE test13")

console.time("FALSE test14")
    i=0;
    for(;i<100000000;i=i+1){
        false + 0;
    }
console.timeEnd("FALSE test14")

console.log("----------------------------")

console.time("TRUE test15")
    i=0;
    for(;i<100000000;i=i+1){
        true ^ 0;
    }
console.timeEnd("TRUE test15")

console.time("FALSE test16")
    i=0;
    for(;i<100000000;i=i+1){
        false ^ 0;
    }
console.timeEnd("FALSE test16")

console.log("----------------------------")

console.time("TRUE test17")
    i=0;
    for(;i<100000000;i=i+1){
        true ^ 0;
    }
console.timeEnd("TRUE test17")

console.time("FALSE test18")
    i=0;
    for(;i<100000000;i=i+1){
        false ^ 0;
    }
console.timeEnd("FALSE test18")

console.log("----------------------------")

console.time("TRUE test19")
    i=0;
    for(;i<100000000;i=i+1){
        true >> 0;
    }
console.timeEnd("TRUE test19")

console.time("FALSE test20")
    i=0;
    for(;i<100000000;i=i+1){
        false >> 0;
    }
console.timeEnd("FALSE test20")

console.log("----------------------------")

console.time("TRUE test21")
    i=0;
    for(;i<100000000;i=i+1){
        true >>> 0;
    }
console.timeEnd("TRUE test21")

console.time("FALSE test22")
    i=0;
    for(;i<100000000;i=i+1){
        false >>> 0;
    }
console.timeEnd("FALSE test22")

console.log("----------------------------")

console.time("TRUE test23")
    i=0;
    for(;i<100000000;i=i+1){
        true << 0;
    }
console.timeEnd("TRUE test23")

console.time("FALSE test24")
    i=0;
    for(;i<100000000;i=i+1){
        false << 0;
    }
console.timeEnd("FALSE test24")

console.log("----------------------------")

console.time("TRUE test25")
    i=0;
    for(;i<100000000;i=i+1){
        ~~true;
    }
console.timeEnd("TRUE test25")

console.time("FALSE test26")
    i=0;
    for(;i<100000000;i=i+1){
        ~~false;
    }
console.timeEnd("FALSE test26")

console.log("----------------------------")

console.time("TRUE test25.1")
    i=0;
    for(;i<100000000;i=i+1){
        ~true*-1-1;
    }
console.timeEnd("TRUE test25.1")

console.time("FALSE test26.1")
    i=0;
    for(;i<100000000;i=i+1){
        ~false*-1-1;
    }
console.timeEnd("FALSE test26.1")

console.log("----------------------------")

console.time("TRUE test27")
    i=0;
    for(;i<100000000;i=i+1){
        true/1;
    }
console.timeEnd("TRUE test27")

console.time("FALSE test28")
    i=0;
    for(;i<100000000;i=i+1){
        false/1;
    }
console.timeEnd("FALSE test28")

结果

TRUE test1: 93.301ms
FALSE test2: 102.854ms
----------------------------
TRUE test1.1: 118.979ms
FALSE test2.1: 119.061ms
----------------------------
TRUE test3: 97.265ms
FALSE test4: 108.389ms
----------------------------
TRUE test5: 85.854ms
FALSE test6: 87.449ms
----------------------------
TRUE test7: 83.126ms
FALSE test8: 84.992ms
----------------------------
TRUE test9: 99.683ms
FALSE test10: 87.080ms
----------------------------
TRUE test9.1: 85.587ms
FALSE test10.1: 86.050ms
----------------------------
TRUE test9.2: 85.883ms
FALSE test10.2: 89.066ms
----------------------------
TRUE test9.3: 86.722ms
FALSE test10.3: 85.187ms
----------------------------
TRUE test11: 86.245ms
FALSE test12: 85.808ms
----------------------------
TRUE test13: 84.192ms
FALSE test14: 84.173ms
----------------------------
TRUE test15: 81.575ms
FALSE test16: 81.699ms
----------------------------
TRUE test17: 81.979ms
FALSE test18: 81.599ms
----------------------------
TRUE test19: 81.578ms
FALSE test20: 81.452ms
----------------------------
TRUE test21: 115.886ms
FALSE test22: 88.935ms
----------------------------
TRUE test23: 82.077ms
FALSE test24: 81.822ms
----------------------------
TRUE test25: 81.904ms
FALSE test26: 82.371ms
----------------------------
TRUE test25.1: 82.319ms
FALSE test26.1: 96.648ms
----------------------------
TRUE test27: 89.943ms
FALSE test28: 83.646ms

我更喜欢用数字函数。它接受一个对象并将其转换为一个数字。

例子:

var myFalseBool = false;
var myTrueBool = true;

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

var myTrueInt = Number(myTrueBool);
console.log(myTrueInt === 1);

你可以在jsFiddle中测试它。

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

TL;DR -当前所有浏览器的最佳选择是:

val | 0;

.

更新:

似乎这些天它们都是完全相同的,除了Number()函数是最慢的,而最好的val === true ?1: 0;。