位运算用在最基本的层次,是直接用内存中的数值的位来操作数值。
js所有的数字都以IEEE-754 64位存储,但在实际运算中,我们是操作32位的,
这个过程是这样的。
64位存储-->32位操作-->64位存储
js中的数值分为有符号数和无符号数,区别就在于第32位数字用来表示符号还是数值,用来表示符号就是有符号数,可以表示正负数;没有符号就是无符号数,只有正数,但可以表示更大的值。
有符号位整数
32位中的前31位用来表示数值,而第32位用于表示数值的符号,0代表正数、1代表负数。
负数是以二进制补码的形式存储的,其中正数和负数的转化形式是这样的。
- 首先是将这位数的绝对值转化为二进制
(如-18绝对值的二进制是0000 0000 0000 0000 0000 0000 0001 0010 ) - 求二进制补码,即0变成1,1变成0(1111 1111 1111 1111 1111 1111 1110 1101)
- 最后是反码加一变成补码,即求结果(1111 1111 1111 1111 1111 1111 1110 1110 )
ECMAScript会隐藏这些细节,一般我们会看到在二进制前面加一个负号来表示负数
var num=-18;
console.log(num.toString(2))
// "-10010"
-
按位非(NOT)
操作符是(~),执行这个操作的结果就是返回数值的反码
var num1 = 25;
// 00000000000000000000000000011001
var num2 = ~num1;
// 11111111111111111111111111100110
console.log(num2)
// -26
类似的,我们用负号运算,这体现了按位非的本质。
var num1 = 25;
var num2 = -num1 - 1;
console.log(num2)
// -26
-
按位与(AND)
操作符是(&),规则就是将两个数值对齐,然后按照下面的规则返回结果
第一个数值的值 | 第二个数值的值 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
简而言之,按位与操作就是两个数值都是1的时候才返回1,否则返回0;
var result = 25 & 3;
console.log(result);
// 1
// 25 = 1 1 0 0 1
// 3 = 0 0 0 1 1
// ↓ ↓ ↓ ↓ ↓
// AND = 0 0 0 0 1
-
按位或(OR)
第一个数值的值 | 第二个数值的值 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
操作符是(|),规则遵循下表
第一个数值的值 | 第二个数值的值 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
简而言之,按位或就是有一位数值是1就返回1,只有全为0的时候才返回0。
var result = 25 | 3;
console.log(result);
// 27
// 25 = 1 1 0 0 1
// 3 = 0 0 0 1 1
// ↓ ↓ ↓ ↓ ↓
// OR = 1 1 0 1 1
-
按位异或(XOR)
第一个数值的值 | 第二个数值的值 | 结果 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
操作符是(^),操作数是两个,规则如下:
第一个数值的值 | 第二个数值的值 | 结果 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
按位异或和按位或的不同之处就在,当两个数值都是1的时候,前者返回0而后者返回1。其他的方面两个都是一样的,所以按位异或就是当只有一个1的时候才返回1。
var result = 25 ^ 3;
console.log(result);
// 26
// 25 = 1 1 0 0 1
// 3 = 0 0 0 1 1
// ↓ ↓ ↓ ↓ ↓
// XOR = 1 1 0 1 0
-
左移
操作符是两个小于号(<<),左移表示将指定二进制数向左移动指定的位数。比如下面例子将2向左移动5位
var value = 2;
var result = value << 5;
console.log(result);
// 64
// 00010 → 0001000000
向左移位后,右侧会多出5个空位,这些空位将会以0填充。
注意,左移不会影响符号位,意思就是符号位是独立的,不会改变。例如-2左移5位,得到的是-64而非64。
var value = -2;
var result = value << 5;
console.log(result);
// -64
-
有符号右移
操作符是两个大于号(>>),右移表示将指定二进制数向右移动指定的位数。比如下面例子将64向右移动5位
var value = 64;
var result = value >> 5;
console.log(result);
// 2
// 00000000000000000000000001000000
// 00000000000000000000000000000010
同样的,在位移过程中,左侧会出现空位,这时候会用符号位的值来填充所有空位,以便得到一个完整的值。
var value = -64;
var result = value >> 5;
console.log(result);
// -2
// 11111111111111111111111110000000
// 11111111111111111111111111111110
-
无符号右移
无符号右移的操作符是3个大于号(>>>),
这个操作符会将所有32位数值向右移动,右侧空位以0填充。
var value1 = 64;
var result1 = value1 >>> 5;
console.log(result1);
// 2
var value2 = -64;
var result2 = value2 >>> 5;
console.log(result2);
// 134217726
//
// 11111111111111111111111111100000
// 无符号左移5位
// 00000111111111111111111111111110
无符号右移会把负数的二进制码当成正数的二进制码,所以这会导致负数的无符号右移结果特别大。