这一篇继续来说《高程四》的第三章。目前是看到了3.5.2的位操作符这里。
2. 按位与
按位与操作符用和号( & )表示,有两个操作数。本质上,按位与就是将两个数的每一个位对齐,然后基于真值表中的规则,对每一位执行相应的与操作。(也就是把两个操作数进行逐位的与运算,与运算就是“两个值都为1才得到1,否则都得到0”的运算)
3. 按位或
按位或操作符用管道符( | )表示,同样有两个操作数。(就是对两个操作数的每一位进行或运算,或运算就是“两个值中存在1,就返回1”的运算)
4. 按位异或
按位异或用脱字符( ^ )表示,同样有两个操作数。(就是对两个操作数的每一位进行异或运算,异或运算就是“两个值相同返回0,不同返回1”的运算)
5. 左移
左移操作符用两个小于号( << )表示,会按照指定的位数将数值的所有位向左移动。(因为把数值的位向左移动n位后,右边的低位会用0来填充,所以相当于扩大了2的n次方倍)
注意,左移会保留它所操作数值的符号。比如,如果-2左移5位,将得到-64,而不是正64。
let oldValue = 2; // 等于二进制10
let newValue = oldValue << 5; // 等于二进制1000000,即十进制64
6. 有符号右移
有符号右移由两个大于号( >> )表示,会将数值的所有32位都向右移,同时保留符号(正或负)。有符号右移实际上是左移的逆运算。
let oldValue = 64; // 等于二进制1000000
let newValue = oldValue >> 5; // 等于二进制10,即十进制2
同样,移位后就会出现空位。不过,右移后空位会出现在左侧,且在符号位之后。ECMAScript会用符号位的值来填充这些空位,以得到完整的数值
7. 无符号右移
无符号右移用3个大于号表示( >>> ),会将数值的所有32位都向右移。对于正数,无符号右移与有符号右移结果相同。
对于负数,有时候差异会非常大。与有符号右移不同,无符号右移会给空位补0,而不管符号位是什么。对正数来说,这跟有符号右移效果相同。但对负数来说,结果就差太多了。无符号右移操作符将负数的二进制表示当成正数的二进制表示来处理。因为负数是其绝对值的二补数,所以右移之后结果变得非常之大。
比如:
let oldValue = -64; // 等于二进制11111111111111111111111111000000
let newValue = oldValue >>> 5; // 等于十进制134217726
在对-64无符号右移5位后,结果是134 217 726。这是因为-64的二进制表示是11111111111111111111111111000000,无符号右移却将它当成正值,也就是4 294 967 232。把这个值右移5位后,结果是00000111111111111111111111111110,即134 217 726。
3.5.3 布尔操作符
对于编程语言来说,布尔操作符跟相等操作符几乎同样重要。如果没有能力测试两个值的关系,那么像 if-else 和循环这样的语句也没什么用了。布尔操作符一共有3个:逻辑非、逻辑与和逻辑或。
1. 逻辑非
逻辑非操作符由一个叹号( ! )表示,可应用给ECMAScript中的任何值。这个操作符始终返回布尔值,无论应用到的是什么数据类型。
逻辑非操作符会遵循如下规则——(对真值返回false,对假值返回true)
如果操作数是对象,则返回 false 。
如果操作数是空字符串,则返回 true 。
如果操作数是非空字符串,则返回 false 。
如果操作数是数值0,则返回 true 。
如果操作数是非0数值(包括 Infinity ),则返回false 。
如果操作数是 null ,则返回 true 。
如果操作数是 NaN ,则返回 true 。
如果操作数是 undefined ,则返回 true 。
逻辑非操作符也可以用于把任意值转换为布尔值。同时使用两个叹号( !! ),相当于调用了转型函数 Boolean() 。无论操作数是什么类型,第一个叹号总会返回布尔值。第二个叹号对该布尔值取反,从而给出变量真正对应的布尔值。结果与对同一个值使用 Boolean() 函数是一样的。
2. 逻辑与
逻辑与操作符由两个和号( && )表示,应用到两个值。
逻辑与操作符遵循如下真值表:
第一个操作数 第二个操作数 结果 true true true true false false false true false false false false
逻辑与操作符可用于任何类型的操作数,不限于布尔值。如果有操作数不是布尔值,则逻辑与并不一定会返回布尔值,而是遵循如下规则。(返回的默认是第一个假值,如果两个操作数都是真值,那么返回的是后一个真值)
如果第一个操作数是对象,则返回第二个操作数。
如果第二个操作数是对象,则只有第一个操作数求值为true 才会返回该对象。
如果两个操作数都是对象,则返回第二个操作数。
如果有一个操作数是 null ,则返回 null 。
如果有一个操作数是 NaN ,则返回 NaN 。
如果有一个操作数是 undefined ,则返回 undefined
逻辑与操作符是一种短路操作符,意思就是如果第一个操作数决定了结果,那么永远不会对第二个操作数求值。对逻辑与操作符来说,如果第一个操作数是 false ,那么无论第二个操作数是什么值,结果也不可能等于 true 。
3. 逻辑或
逻辑或操作符由两个管道符( || )表示。
逻辑或操作符遵循如下真值表:(两个操作数里有一个是true,返回的结果就是true)
第一个操作数 第二个操作数 结果 true true true true false true false true true false false false
与逻辑与类似,如果有一个操作数不是布尔值,那么逻辑或操作符也不一定返回布尔值。它遵循如下规则:
如果第一个操作数是对象,则返回第一个操作数。
如果第一个操作数求值为 false ,则返回第二个操作数。
如果两个操作数都是对象,则返回第一个操作数。
如果两个操作数都是 null ,则返回 null 。
如果两个操作数都是 NaN ,则返回 NaN 。
如果两个操作数都是 undefined ,则返回 undefined
同样与逻辑与类似,逻辑或操作符也具有短路的特性。只不过对逻辑或而言,第一个操作数求值为 true ,第二个操作数就不会再被求值了。
利用这个行为,可以避免给变量赋值 null 或 undefined 。比如:
let myObject = preferredObject || backupObject;
在这个例子中,变量 myObject 会被赋予两个值中的一个。其中, preferredObject 变量包含首选的值,backupObject 变量包含备用的值。如果preferredObject 不是 null ,则它的值就会赋给myObject ;如果 preferredObject 是 null ,则backupObject 的值就会赋给 myObject 。这种模式在ECMAScript代码中经常用于变量赋值。