在 JavaScript 中,我应该在哪里使用位运算符?

80

我读过'什么是位运算符?',所以我知道位运算符是什么,但我还不清楚在 JavaScript 中如何使用它们。有人能提供任何在实际应用中使用位运算符的例子吗?

谢谢。

编辑:

刚刚深入研究了jQuery源代码,我发现了一些使用位运算符的地方,例如:(只有 & 运算符)

// Line 2756:
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));

// Line 2101
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
16个回答

75

示例:

将十六进制值解析为RGB颜色值。

var hex = 'ffaadd';
var rgb = parseInt(hex, 16); // rgb is 16755421


var red   = (rgb >> 16) & 0xFF; // returns 255
var green = (rgb >> 8) & 0xFF;  // 170
var blue  = rgb & 0xFF;     // 221  

这与UTF-16的兼容性如何? - Sebastian Barth
10
@SebastianBarth怎么不相关呢?这段代码中根本没有任何与字符编码相关的部分。 - hobbs

48

我在生产脚本中大量使用按位运算符进行数字转换,因为有时它们比其相应的 MathparseInt 速度更快。

代价是代码可读性受到影响,所以我通常在开发中使用Math,生产中使用按位运算。

您可以在jsperf.com上找到一些性能技巧

正如您所看到的,浏览器多年来未优化Math.ceilparseInt,因此我预测按位运算在未来将是更快、更短的处理方式

在SO上进一步阅读...


附加: | 0速查表 :将任何内容转换为整数的简单快速方法:

( 3|0 ) === 3;             // it does not change integers
( 3.3|0 ) === 3;           // it casts off the fractional part in fractionalal numbers
( 3.8|0 ) === 3;           // it does not round, but exactly casts off the fractional part
( -3.3|0 ) === -3;         // including negative fractional numbers
( -3.8|0 ) === -3;         // which have Math.floor(-3.3) == Math.floor(-3.8) == -4
( "3"|0 ) === 3;           // strings with numbers are typecast to integers
( "3.8"|0 ) === 3;         // during this the fractional part is cast off too
( "-3.8"|0 ) === -3;       // including negative fractional numbers
( NaN|0 ) === 0;           // NaN is typecast to 0
( Infinity|0 ) === 0;      // the typecast to 0 occurs with the Infinity
( -Infinity|0 ) === 0;     // and with -Infinity
( null|0 ) === 0;          // and with null,
( (void 0)|0 ) === 0;      // and with undefined
( []|0 ) === 0;            // and with an empty array
( [3]|0 ) === 3;           // but an array with one number is typecast to number
( [-3.8]|0 ) === -3;       // including the cast off of the fractional part
( [" -3.8 "]|0 ) === -3;   // including the typecast of strings to numbers
( [-3.8, 22]|0 ) === 0     // but an Array with several numbers is typecast to 0
( {}|0 ) === 0;                // an empty object is typecast to 0
( {'2':'3'}|0 ) === 0;         // or a not empty object
( (function(){})|0 ) === 0;    // an empty function is typecast to 0 too
( (function(){ return 3;})|0 ) === 0;

还有一些神奇的东西是属于我的:

3 | '0px' === 3;

使用高级模式的闭包编译器,它可以为您执行所有这些优化。 - Pawel
2
这不是一个好的答案。起初它看起来像是一个参考资料,提供了很多信息(我甚至昨天还点赞了它),但经过更仔细的查看后发现这并不是真的。 - Dmitry Koroliov
4
这大约20行的示例可以用一句话表达:按照以下规则将任何一个操作数转换为32位二进制补码大端(32位简称)表示法,然后再使用按位运算符进行运算,这些运算符仅适用于32位表示法:对于数字,将其从IEEE-754 64位格式转换为32位格式;对于其他所有内容,首先按照ECMAScript规范将其转换为数字(例如,对于对象(即对象、数组、函数),通过调用其valueOf方法),然后将该数字转换为32位格式。 - Dmitry Koroliov
1
你可以尝试这个代码:'use strict'; Function.prototype.valueOf = function() {return 2;};console.log(( (function(){})|0 ) === 0);,你会发现你的最后两个例子是不正确的等等。 - Dmitry Koroliov
@Pacerier,不,我不会。 - Dmitry Koroliov
4
实际上,最好同时提供单个句子和代码行。这个句子会简明地解释给像你一样的高级程序员,但对于像我这样从未使用过其他语言且只了解JavaScript的人来说,你所谈论的32位、二进制补码和大端是难以理解的。我是一个动手学习者,代码完全帮助了我(尽管可以少写那么多重复的部分)。 - Marcus Hughes

25
在JavaScript中,您可以使用双重按位否定(~~n)代替Math.floor(n)(如果n是正数),或者parseInt(n, 10)(即使n是负数)。n|nn&n总是产生与~~n相同的结果。
var n = Math.PI;
n; // 3.141592653589793
Math.floor(n); // 3
parseInt(n, 10); // 3
~~n; // 3
n|n; // 3
n&n; // 3

// ~~n works as a replacement for parseInt() with negative numbers…
~~(-n); // -3
(-n)|(-n); // -3
(-n)&(-n); // -3
parseInt(-n, 10); // -3
// …although it doesn’t replace Math.floor() for negative numbers
Math.floor(-n); // -4

一个按位非运算符(~)计算-(parseInt(n, 10) + 1),因此两个按位非运算符将返回-(-(parseInt(n, 10) + 1) + 1)
需要注意的是,在这三种替代方案中,n|n似乎是最快的更新: 更准确的基准测试结果在这里: http://jsperf.com/rounding-numbers-down (如在Strangest language feature的帖子中发布的)

1
“n << 0;”现在在V8上是最快的。“n | 0;”非常接近,并且这是我使用的方法。 - Bardi Harborow
@BardiHarborow,n >>> 0怎么样? - Pacerier
与其问“你如何使用它”,更相关的问题可能是:“为什么要使用它?”这个功能是否有任何潜在的性能提升值得牺牲代码可读性? - Ryan
你能解释一下为什么每次使用位运算符时小数点都会被截断吗?位运算符只适用于整数吗? - Captainlonate
@Ryan 代码的可读性可以轻松通过双斜杠后面跟着文字进行修正,例如 wetMop = n|0; // bitwise version of Math.floor - Myndex
虽然它不能替代负数的 Math.floor(),那么这是否意味着它更像是 Math.trunc(number) 呢? - Samathingamajig

24

^ 位运算异或作为开关

value ^= 1 在每次调用时将 value 更改为 0, 1, 0, 1, ... ,(基本上类似于:boolVal != boolVal):

function toggle(evt) {
  const EL = evt.currentTarget;
  EL.isOn ^= 1; // Bitwise toggle
  EL.textContent = EL.isOn ? "ON" : "OFF"; // Unleash your ideas
}

document.querySelectorAll("button").forEach( el =>
  el.addEventListener("click", toggle)
);
<button>OFF</button>
<button>OFF</button>
<button>OFF</button>


16

判断一个数是否为奇数:

function isOdd(number) {
    return !!(number & 1);
}

isOdd(1); // true, 1 is odd
isOdd(2); // false, 2 is not odd
isOdd(357); // true, 357 is odd

比模数运算更快 - 适用于性能真正重要的地方!


4
为什么不这样写: function isOdd(number) { return number % 2; } (该函数用于判断给定数字是否为奇数,将输入数字除以2的余数返回。原始代码有错别字并使用了未定义的变量x,已修正。) - Duc Filan
2
性能。https://jsperf.com/modulo-vs-bitwise/8 在大多数浏览器中,按位运算比取模更快(或者说,在回答发布时是这样,在最近的Chrome中没有太大的区别)。 - danwellman

16

随着JavaScript的不断发展(特别是通过nodejs使得使用JS进行服务器端编程成为可能),JS中的代码变得越来越复杂。以下是我使用位运算符的一些情况:

  • IP地址操作:

    //computes the broadcast address based on the mask and a host address
    broadcast = (ip & mask) | (mask ^ 0xFFFFFFFF)
    
    
    //converts a number to an ip adress 
    sprintf(ip, "%i.%i.%i.%i", ((ip_int >> 24) & 0x000000FF),
                             ((ip_int >> 16) & 0x000000FF),
                             ((ip_int >>  8) & 0x000000FF),
                             ( ip_int        & 0x000000FF));
    

注意:这是C代码,但JS几乎相同。

  • CRC算法经常使用它们

请查看关于此的维基百科条目

  • 屏幕分辨率操作

你有在这些情况下使用位运算符的示例吗? - thomasrutter
我了解,但是我不在开源领域工作,所以无法指向代码。 - Bogdan Gavril MSFT

13

以下是使用位取反和双重位取反的几个示例:

向下取整操作

~~2.5    // 2
~~2.1    // 2
~~(-2.5) // -2

检查indexOf方法是否返回了-1

var foo = 'abc';
!~foo.indexOf('bar'); // true

11

您可以使用它们来翻转布尔值:

var foo = 1;
var bar = 0;
alert(foo ^= 1);
alert(bar ^= 1);

不过实际上,在Javascript中,位运算符并没有太多的应用。这有点儿可笑。


9
不傻。我经常使用它来循环切换图片、分隔线等的“开/关”状态。 - Crescent Fresh

8
var arr = ['abc', 'xyz']

写作很烦人

if (arr.indexOf('abc') > -1) {
  // 'abc' is in arr
}

if (arr.indexOf('def') === -1) {
  // 'def' is not in arr
}

如何检查某个元素是否在数组中?

你可以使用按位运算符~,例如:

if (~arr.indexOf('abc')) {
  // 'abc' is in arr
}

if (! ~arr.indexOf('def')) {
  // 'def' is not in arr
}

4
自ES2016以来,也可以使用arr.includes方法。 - Yann Bertrand

4

位掩码

在JS事件中被广泛使用。


9
可以请您举个例子吗? - Alex Grande

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