ECMAScript
中的操作符是独特的,因为他们可用于各种值,包括字符串、数值、布尔值,甚至还有对象。
一元操作符
只操作一个值的操作符叫一元操作符。
递增递减操作符
递增递减操作符非常方便,但也有很大的误区
let age = 12
console.log(++age) //13 //前缀递增操作符
console.log(--age) //12 //前缀递减操作符
console.log(age++) //会打印什么?13? 不对,还是12
console.log(age) //13 没错,后缀++会在该语句被求值之后才会执行改变
//同理后缀递减
console.log(age--) //13 求值之后再执行--
console.log(age) //12
如果操作的不是整数,将遵循如下规则
- 对于字符串,会使用和
Number()
转型函数相同的规则 - 对于布尔值,
false
转换为0,true
装换为1 - 如果是对象,则调用
valueOf()
。然后再应用上述规则。如果是NaN
则调用toString()
并再次应用其他规则
以上变量类型在执行操作以后,都会从原来的类型变为数值类型
let s1 = "2";
let s2 = "z";
let b = false;
let f = 1.1;
let o = {
valueOf() {
return -1;
}
};
s1++; // 3 直接转换为数值再执行++
s2++; // NaN 转换为NaN再执行++
b++; // 1 转换为0再执行++
f--; // 值变成 0.10000000000000009(因为浮点数不精确)
o--; // -2 调用了valueOf()方法
一元加/减
一元加放在数值变量前,对数值没有任何影响,一元减操作符放在数值变量前,数值会变成相反的数值。同样,一元加/减操作符也可以放在其他数据类型前面
+false //0
-true //-1
+'01' //1
+'z' //NaN
//都执行与使用Number()转型函数一样的类型转换
位操作符
ECMAScript
中的所有数值都以 IEEE 754 64
位格式存储,但位操作并不直接应用到 64
位表示,而是先把值转换为 32
位整数,再进行位操作,之后再把结果转换为 64
位。对开发者而言,就好像只有 32
位整数一样,因 为 64
位整数存储格式是不可见的。既然知道了这些,就只需要考虑 32
位整数即可
有符号整数使用 32
位的前 31
位表示整数值。第 32
位表示数值的符号,如 0
表示正,1
表示负
负值以一种称为二补数(或补码)的二进制编码存储
获得补码:
- 获取负值的绝对值二进制表示
- 将二进制取反码
- 再在反码的基础上加一获得补码
按位非
按位非操作符使用波浪符(~)
,返回数值的一补数(反码)
let num1 = 25; // 二进制 00000000000000000000000000011001
let num2 = ~num1; // 二进制 11111111111111111111111111100110
console.log(num2); // -26
//按位非操作最终效果,其实是对数值取反并减1,类似于
let num1 = 25;
let num2 = -num1 - 1;
console.log(num2); // -26
按位与
按位与操作符使用和号(&)
,有两个操作数。
第一个数值的位 | 第二个数值的位 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 0 |
0 | 1 | 0 |
0 | 0 | 0 |
只有对应两个位全为1
,其结果才为1
按位或
按位或操作符使用管道符(|)
,有两个操作数
第一个数值的位 | 第二个数值的位 | 结果 |
---|---|---|
1 | 1 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
只要对应位有一个为1
,其结果就为1
按位异或
按位异或使用脱字符(^)
,有两个操作数
第一个数值的位 | 第二个数值的位 | 结果 |
---|---|---|
1 | 1 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
0 | 0 | 0 |
只要两个位不相同,其结果就为1
左移和右移
左移使用符号<<
,有符号右移使用>>
,无符号右移使用>>>
左移不区分有无符号,都在最后边补0
有符号到的右移,在左边补符号位,而无符号的右移在左边补0
let oldValue = -64; // 等于二进制 11111111111111111111111111000000
let newValue = oldValue >>> 5; // 等于十进制 134217726
//这是因为-64 的二进制表示是 11111111111111111111111111000000,无符号右移却将它当成正值,也就是 4 294 967 232。
//把这个值右移 5 位后,结果是00000111111111111111111111111110,即 134 217 726。
布尔操作符
布尔操作符一共有 3 个:逻辑非、逻辑与和逻辑或。
逻辑非
逻辑非操作符由一个叹号(!)
表示,可应用给 ECMAScript
中的任何值
逻辑非,先使用与Boolean()
转型函数相似的功能,然后再取反
console.log(!false); // true
console.log(!"blue"); // false
console.log(!0); // true
console.log(!NaN); // true
console.log(!""); // true
console.log(!12345); // false
//同时使用两个叹号(!!),相当于调用了转型函数 Boolean()
console.log(!!"blue"); // true
console.log(!!0); // false
console.log(!!NaN); // false
console.log(!!""); // false
console.log(!!12345); // true
逻辑与
逻辑与操作符由两个和号(&&)
表示,应用到两个值
规则:
- 如果第一个操作数是对象,则返回第二个操作数
- 如果第二个操作数是对象,则只有第一个操作数是
true
的前提才返回该对象 - 如果两个操作数都是对象,则返回第二个对象
其实以上两句可以理解为只有当第一个参数为
true
,就返回第二个参数。对象在转换为布尔值时,都是true
。 - 如果有一个操作数是
null
,返回null
- 如果有一个操作数是
NaN
,返回NaN
- 如果有一个操作数是
undefined
,返回undefined
其实以上类型转换为布尔值都为
false
。由于短路效应,所以就返回第一个操作数
短路效应,当一个操作数就能决定结果时,将不会执行第二个操作数。 下面逻辑或也是如此
逻辑或
逻辑或操作数由两个管道符(||)
表示。
规则:
- 如果第一个操作数是对象,则返回该对象
- 如果第一个操作数是
false
,则返回第二个操作数 - 如果两个操作数都是对象,则返回第一个操作数
- 如果两个操作数都是
null
,则返回null
- 如果两个操作数都是
undefined
,则返回undefined
- 如果两个操作数都是
NaN
,则返回NaN
其实是短路效应
乘性操作符
乘性操作符包括乘法,除法和取模,对于非数值的操作数,会被Number()
隐式转换为数值
乘法操作符
使用*
表示,特殊值应用规则
-
操作数都是数值则正常操作。如果
ECMAScript
不能表示乘积,则返回Infinity
或-Infinity
-
如果有一个操作数是
NaN
,则值为NaN
与
NaN
的操作都会返回NaN
-
如果是
Infinity
乘以0
,则返回NaN
-
Infinity
乘以非0
数返回Infinity
或-Infinity
-
如果有不是数值的操作数,则先后台用
Number()
将其转换为数值,然后再应用上述规则
除法操作符
使用/
表示,特殊值应用规则:
- 操作数都是数值则正常操作。如果
ECMAScript
不能表示商,则返回Infinity
或-Infinity
- 如果有任意操作数是
NaN
,则返回NaN
- 如果是
Infinity
除以Infinity
则返回NaN
- 如果是
0
除以0
,则返回NaN
- 如果是非
0
的有限值除以0
则根据第一个操作数的符号返回Infinity
或-Infinity
- 如果是
Infinity
除以任何数值,则根据第二个操作数的符号返回Infinity
或-Infinity
- 如果有不是数值的操作数,则先后台用
Number()
将其转换为数值,然后再应用上述规则
取模操作符
使用一个%
表示,规则如下
- 如果操作数是数值,则执行常规除法运算,返回余数
- 如果被除数是无限值,除数是有限值,则返回
NaN
- 如果被除数是有限值,除数是
0
,则返回NaN
- 如果是 Infinity 除以 Infinity,则返回 NaN。
- 如果被除数是有限值,除数是无限值,则返回被除数。
- 如果被除数是 0,除数不是 0,则返回 0。
- 如果有不是数值的操作数,则先在后台用 Number()函数将其转换为数值,然后再应用上述规则。