位运算

概述

按位操作符(Bitwise operators) 将其操作数(operands)当作32位的比特序列(由0和1组成),而不是十进制、十六进制或八进制数值。例如,十进制数9,用二进制表示则为1001。按位操作符操作数字的二进制形式,但是返回值依然是标准的JavaScript数值。

位运算_第1张图片
按位操作符

有符号 32 位整数

  1. 二进制编码

    // 其中 0b 代表二进制
     314 (base 10) = 0b00000000000000000000000100111010 (base 2)
    
  2. 反码

     314 (base 10) = 0b00000000000000000000000100111010 (base 2)
    ~314 (base 10) = 0b11111111111111111111111011000101 (base 2)
    
  3. 补码

     314 (base 10)  = 0b00000000000000000000000100111010 (base 2)
    ~314 (base 10)  = 0b11111111111111111111111011000101 (base 2)
    // -314 = 314 反码 + 1
    -314 (base 10)  = 0b11111111111111111111111011000110 (base 2)
    
  4. 符号位
    补码保证了当一个数是正数时,其最左的比特位是0,当一个数是负数时,其最左的比特位是1。因此,最左边的比特位被称为符号位

  5. 特殊数字

     0 (base 10) = 00000000000000000000000000000000 (base 2)
    -1 (base 10) = 11111111111111111111111111111111 (base 2)
    

按位逻辑操作符

从概念上讲,按位逻辑操作符按遵守下面规则:

  • 操作数被转换成32位整数,用比特序列(0和1组成)表示。超过32位的数字会被丢弃。
  • 第一个操作数的每个比特位与第二个操作数的相应比特位匹配:第一位对应第一位,第二位对应第二位,以此类推。
  • 位运算符应用到每对比特位,结果是新的比特值。

& (按位与)

a b a & b
0 0 0
0 1 0
1 0 0
1 1 1
     9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 & 9 (base 10) = 00000000000000000000000000001000 (base 2) = 8 (base 10)

注意:

  • 0 & x === 0
  • -1 & x === x

| (按位或)

a b a 或 b
0 0 0
0 1 1
1 0 1
1 1 1
     9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 | 9 (base 10) = 00000000000000000000000000001111 (base 2) = 15 (base 10)

注意:

  • 0 | x === x
  • -1 | x === -1
  • 2.2341543 | 0 === 2
  • 'adkfajdsf' | 0 === 0
  • '123456' | 0 === 123456

~ (按位非)

a ~a
0 1
1 0
 9 (base 10) = 00000000000000000000000000001001 (base 2)
               --------------------------------
~9 (base 10) = 11111111111111111111111111110110 (base 2) = -10 (base 10)

注意:

  • ~x === -(x + 1)
  • ~-1 === 0

^ (按位异或)

a b a ^ b
0 0 0
0 1 1
1 0 1
1 1 0
     9 (base 10) = 00000000000000000000000000001001 (base 2)
    14 (base 10) = 00000000000000000000000000001110 (base 2)
                   --------------------------------
14 ^ 9 (base 10) = 00000000000000000000000000000111 (base 2) = 7 (base 10)

注意:

  • 0 ^ x === x
  • -1 ^ x === ~x

标志位与掩码

标志位

位运算经常被用来创建、处理以及读取标志位序列——一种类似二进制的变量。标志位序列可以节省内存

例如,有 4 个标志位:

  • 标志位 A:我们有 ant
  • 标志位 B:我们有 bat
  • 标志位 C:我们有 cat
  • 标志位 D:我们有 duck

标志位通过位序列 DCBA 来表示

  • 当一个位被置位 (set) 时,它的值为 1
  • 当一个位被清除 (clear) 时,它的值为 0

例如:一个变量 flags 的二进制值为 0101:

const flags = 0b0101;

这个值表示:

  • 标志位 A 是 true (我们有 ant);
  • 标志位 B 是 false (我们没有 bat);
  • 标志位 C 是 true (我们有 cat);
  • 标志位 D 是 false (我们没有 duck);

掩码

掩码 (bitmask) 是一个通过与 | 或来读取标志位的位序列。典型的定义每个标志位的原语掩码如下:

const flag_A = 0b0001;
const flag_B = 0b0010;
const flag_C = 0b0100;
const flag_D = 0b1000;

新的掩码可以在以上掩码上使用逻辑运算创建。

应用:

  • 某个特定的位可以通过与掩码做 &运算得到,通过与掩码的 & 运算可以去掉无关的位,得到特定的位
    const flags = 0b0101;
    if (flags & flag_C) { // 0101 & 0100 => 0100 => true
      // do something
    }
    
  • 通过与掩码做 | 运算设置标志位,掩码中为 1 的位可以设置对应的位
    // 掩码 1100 可用来设置位 C 和 D
    let flags = 0b0101;
    const mask = flag_C | flag_D // 0b0100 | 0b1000 => 0b1100
    flags |= mask // 0b0101 | 0b1100 => 0b1101
    
  • 通过与掩码做 & 运算清除标志位,掩码中为 0 的位可以设置对应的位
    // 掩码 1010 可以用来清除标志位 A 和 C 
    let flags = 0b1101;
    const mask = ~(flag_A | flag_C) // ~(0b0001 | 0b0100) => 0b1010
    flags &= mask // 0b1101 & 0b1010 => 0b1000
    
  • 标志位可以使用 ^ 运算切换。所有值为 1 的位可以切换对应的位
    // 掩码 0110 可以用来切换标志位 B 和 C
    let flags = 0b1100;
    const mask = flag_B | flag_C // 0b0010 | 0b0100 => 0b0110
    flags ^= mask // 0b1100 ^ 0b0110 => 0b1010
    
  • 所有标志位可以通过非运算翻转
    let flags = 0b1010;
    flags = ~flags // ~0b1010 => 0b0101
    

德摩根定律

~(a & b) === ~a | ~b
~(a | b) === ~a & ~b

实用技能

~ + indexOf


const str = 'rawr';
const searchFor = 'a';

// ~-1 === 0
if (~str.indexOf(searchFor)) {
  // searchFor 包含在字符串中
} else {
  // searchFor 不包含在字符串中
}

// (~str.indexOf(searchFor))的返回值
// r == -1
// a == -2
// w == -3

通过某个条件来切换一个值为0或者1

let whether = false;
let number = 1;

// whether === true  => num === 0
// whether === false => num === 1
num = whether ^ 1;

// number 在 0 和 1 之间切换
number ^= 1;

判断奇偶

if(a & 1) {
  // 奇数
} else {
   // 偶数
}

你可能感兴趣的:(位运算)