【问题标题】:Where would I use a bitwise operator in JavaScript?在 JavaScript 中我应该在哪里使用位运算符?
【发布时间】:2009-03-17 12:41:15
【问题描述】:

我读过'what are bitwise operators?',所以我知道什么 bitwise operators 是,但我仍然不清楚如何使用它们。任何人都可以提供任何真实世界的例子来说明位运算符在 JavaScript 中的用处吗?

谢谢。

编辑:

刚刚深入了解 jQuery source,我发现了几个使用位运算符的地方,例如:(仅 & 运算符)

// 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;

【问题讨论】:

    标签: javascript bitwise-operators


    【解决方案1】:

    例子:

    解析十六进制值以获取 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?
    • @SebastianBarth 怎么没有?这段代码中没有任何部分与字符编码相关。
    【解决方案2】:

    大量在生产脚本中使用按位运算符进行数值转换,因为有时它们比 MathparseInt 等价物快得多。

    我必须付出的代价是代码可读性。所以我通常在开发和生产中使用Math

    You can find some performance tricks on jsperf.com.

    如您所见,浏览器多年来并未优化 Math.ceilparseInt,因此我预测按位将是更快更短的处理方式 in furure as well

    Some further reading on 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;
    

    【讨论】:

    • 或在高级模式下使用闭包编译器,它会为您完成所有这些优化
    • 这不是一个好的答案。起初它看起来像一个参考资料并且有很多信息(我昨天什至赞成它),但经过更彻底的研究后发现这不是真的。
    • 所有这 20 行左右的示例都可以用一个句子来表达:位运算符使用 32 位二进制补码 big-endian(简称 32 位)数字的表示形式,因此它们的任何操作数都根据以下规则转换为这种格式:数字从 IEEE-754 64 位格式转换为 32 位,其他任何内容首先转换为ECMAScript 规范(即对于对象(即对象、数组、函数)通过​​调用其 valueOf 方法)然后将此数字转换为 32 位格式
    • 你可以试试这个:'use strict'; Function.prototype.valueOf = function() {return 2;};console.log(( (function(){})|0 ) === 0);,你会发现你最后两个例子不正确等等。
    • @user907860:实际上,理想情况下,单句代码行都应该存在。这句话将为像你这样更高级的程序员简明扼要地解释它,但是对于像我这样从未使用过另一种语言并且只知道 Javascript 的人来说,你所有关于 32 位和二进制补码和大端序的讨论都很难理解.我是一个动手实践的学习者,代码完全帮助了我(尽管理所当然,它不需要太多那么重复)。
    【解决方案3】:

    在 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 appears to be the fastest

    更新:此处提供更准确的基准:http://jsperf.com/rounding-numbers-down

    (在Strangest language feature上发布)

    【讨论】:

    • n << 0; 现在是 V8 上最快的。 n | 0; 非常接近,是我使用的。
    • @BardiHarborow,n >>> 0 呢?
    • 比起你如何使用它,一个更相关的问题可能是,“你为什么要使用它?”任何潜在的性能提升值得代码可读性吗?
    • 你能解释一下为什么每次使用按位运算符时小数都会被截断吗?位运算符只对整数起作用吗?
    • @Ryan 代码可读性很容易通过两个斜线后跟单词来纠正,例如wetMop = n|0; // bitwise version of Math.floor
    【解决方案4】:

    ^ 按位异或作为切换器

    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>

    【讨论】:

      【解决方案5】:

      鉴于 Javascript 正在取得的进步(尤其是允许使用 js 进行服务器端编程的 nodejs),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 算法经常使用它们

      在此查看wikipedia entry

      • 屏幕分辨率操作

      【讨论】:

      • 你有在这些情况下使用位运算符的例子吗?
      • 我愿意,但我不在开源领域工作,所以我无法指出代码。
      【解决方案6】:

      判断一个数是否为奇数:

      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
      

      比模数更快 - 在性能真正很重要的地方使用!

      【讨论】:

      • 为什么不:function isOdd(number) { return x % 2; }
      • 性能。 jsperf.com/modulo-vs-bitwise/8 Bitwise 在大多数浏览器上都更快(或者,在发布答案时,在最近的 Chrome 中并没有太大区别 tbf
      【解决方案7】:

      其他几个如何使用按位非和双位非的例子:

      楼层操作

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

      检查 indexOf 是否返回 -1

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

      【讨论】:

        【解决方案8】:

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

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

        这有点傻,而且在大多数情况下,按位运算符在 Javascript 中没有太多应用。

        【讨论】:

        • 这并不傻。我一直用它来循环图像、div 等的“开”/“关”状态。
        【解决方案9】:
        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
        }
        

        【讨论】:

        • arr.includes 从 ES2016 开始也可以使用
        【解决方案10】:

        Bitmasks.

        广泛使用,例如,在 JS 事件中。

        【讨论】:

        • 可以举个例子吗?
        【解决方案11】:

        我用过一次permissions widget。 unix中的文件权限是位掩码,所以要解析它,需要使用位操作。

        【讨论】:

          【解决方案12】:

          这个答案包含Mark's answer的解释。

          通过阅读这些解释并运行代码 sn-p 可以获得一个想法。

          var hex = 'ffaadd';
          var rgb = parseInt(hex, 16); // rgb value is 16755421 in decimal = 111111111010101011011101 in binary = total 24 bits
          
          
          var red   = (rgb >> 16) & 0xFF; // returns 255
          var green = (rgb >> 8) & 0xFF;  // returns 170
          var blue  = rgb & 0xFF;         // returns 221  
          
          // HOW IS IT
          
          // There are two bitwise operation as named SHIFTING and AND operations.
          // SHIFTING is an operation the bits are shifted toward given direction by adding 0 (zero) bit for vacated bit fields.
          // AND is an operation which is the same with multiplying in Math. For instance, if 9th bit of the given first bit-set is 0
          // and 9th bit of the given second bit-set is 1, the new value will be 0 because of 0 x 1 = 0 in math.
          
          // 0xFF (000000000000000011111111 in binary) - used for to evaluate only last 8 bits of a given another bit-set by performing bitwise AND (&) operation. 
          // The count of bits is 24 and the first 16 bits of 0xFF value consist of zero (0) value. Rest of bit-set consists of one (1) value.
          console.log("0xFF \t\t\t\t: ", 0xFF) 
          
          
          // 111111111010101011011101 -> bits of rgb variable
          // 000000000000000011111111 -> 255 after (rgb >> 16) shifting operation
          // 000000000000000011111111 -> 255 complement (changes the first 16 bits and does nothing for the last 8 bits)
          // 000000000000000011111111 -> result bits after performing bitwise & operation
          console.log("Red - (rgb >> 16) & 0xFF \t: ", (rgb >> 16) & 0xFF) // used for to evaluate the first 8 bits
          
          // 111111111010101011011101 -> bits of rgb variable
          // 000000001111111110101010 -> 65450 -> 'ffaa'
          // 000000000000000011111111 -> 255 complement (changes the first 16 bits and does nothing for the last 8 bits)
          // 000000000000000010101010 -> result bits after performing bitwise & operation
          // calculation -> 000000001111111110101010 & 000000000000000011111111 = 000000000000000010101010 = 170 in decimal = 'aa' in hex-decimal
          console.log("Green - (rgb >> 8) & 0xFF \t: ", (rgb >> 8) & 0xFF) // used for to evaluate the middle 8 bits 
          
          // 111111111010101011011101 -> 'ffaadd'
          // 000000000000000011111111 -> 255 complement (changes the first 16 bits and does nothing for the last 8 bits)
          // 000000000000000011011101 -> result bits after performing bitwise & operation 
          // calculation -> 111111111010101011011101 & 000000000000000011111111 = 221 in decimal = 'dd' in hex-decimal
          console.log("Blue - rgb & 0xFF \t\t: ", rgb & 0xFF) // // used for to evaluate the last 8 bits.
          
          console.log("It means that `FFAADD` hex-decimal value specifies the same color with rgb(255, 170, 221)")
          
          /* console.log(red)
          console.log(green)
          console.log(blue) */

          【讨论】:

            【解决方案13】:

            我正在使用它们将三个数字扁平化为 1,作为将多维数组存储在 Uint16Array 中的一种方式。这是我正在开发的体素游戏的 sn-p:

            function Chunk() {
              this._blocks = new Uint16Array(32768);
              this._networkUpdates = [];
            }
            
            Chunk.prototype.getBlock = function(x, y, z) {
              return this._blocks[y + (x << 5) + (z << 10)];
            };
            
            Chunk.prototype.setBlock = function(x, y, z, value) {
              this._blocks[y + (x << 5) + (z << 10)] = value;
              this._networkUpdates.push(value + (y << 15) + (x << 20) + (z << 25));
            };
            
            Chunk.prototype.getUpdates = function() {
              return this._networkUpdates;
            };
            
            Chunk.prototype.processUpdate = function(update) {
              // this._blocks[Math.floor(update / 65536)] = update % 65536;
              this._blocks[update >> 16] = update & 65535;
            };
            
            var chunk = new Chunk();
            chunk.setBlock(10, 5, 4);
            alert(chunk.getBlock(10, 5, 4));
            alert(chunk.getUpdates()[0]);

            【讨论】:

            • 你能解释一下这里的鳕鱼吗? 65535 的值是什么 为什么 (x &lt;&lt; 5) + (z &lt;&lt; 10) 中的 5 和 10
            【解决方案14】:

            当您使用十六进制值和位时,它们似乎非常有用。因为4位可以代表0到F。

            1111 = F 1111 1111 = FF。

            【讨论】:

              【解决方案15】:

              使用 Node.js 的示例

              假设你有一个包含这些内容的文件(称为 multiply.js),你可以运行

              `node multiply <number> <number>`
              

              并获得与对相同的两个数字使用乘法运算符一致的输出。 Mulitply 函数中发生的位移是如何获取表示一个数字的位掩码并使用它来翻转另一个数字中的位以进行快速操作的示例。

              var a, b, input = process.argv.slice(2);
              
              var printUsage = function() {
                console.log('USAGE:');
                console.log('  node multiply <number> <number>');
              }
              
              if(input[0] === '--help') {+
                printUsage();
                process.exit(0);
              }
              
              if(input.length !== 2) {
                printUsage();
                process.exit(9);
              }
              
              if(isNaN(+input[0]) || isNaN(+input[1])) {
                printUsage();
                process.exit(9);
              }
              
              // Okay, safe to proceed
              
              a = parseInt(input[0]),
              b = parseInt(input[1]);
              
              var Multiply = function(a,b) {
                var x = a, y = b, z = 0;
              
                while( x > 0 ) {
                  if(x % 2 === 1) {
                    z = z + y;
                  }
                  y = y << 1;
                  x = x >> 1;
                }
              
                return z;
              }
              
              var result = Multiply(a,b);
              
              console.log(result);
              

              【讨论】:

                【解决方案16】:

                我刚刚发现这个问题试图确认按位 AND 运算符是否也是 Javascript 中的 &amp;

                既然你问了一个例子:

                if ($('input[id="user[privileges]"]').length > 0) {
                    $('#privileges button').each(function () {
                        if (parseInt($('input[id="user[privileges]"]').val()) & parseInt($(this).attr('value'))) {
                            $(this).button('toggle');
                        }
                    });
                }
                

                给定隐藏字段的位掩码值,它使用 jQuery 填充按钮的状态:

                • none = 0
                • user = 1
                • administrator = 2
                • user + administrator = 3

                【讨论】:

                  猜你喜欢
                  • 2020-12-18
                  • 2013-10-27
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-04-07
                  • 2021-07-16
                  相关资源
                  最近更新 更多