Get busy living,or get busy dying.
JavaScript操作符
ECMAScript 操作符与众不同之处在于,它能适用于对象,此时操作符通常会调用对象的 valueOf() 或 toString() 方法以取得可以操作的值。
一元操作符
只能操作一个值的操作符叫一元操作符。
1. 递增和递减操作符
借鉴自 C 语言,前置型会在语句运行时被执行,后置型会在语句结束后执行。
在应用于不同的值时,递增递减操作符遵循以下规则:
- 应用于包含有效数字的字符串,先转换为数字,再执行加减操作,字符串变量变成数值变量
- 应用于不包含有效数字的字符串,将变量的值设置为 NaN,字符串变量变成数值变量(NaN 是特殊的数值类型)
- 应用于布尔值,先转换为数字 0 或 1,再执行加减操作,布尔值变量变成数值变量
- 应用于浮点数值,执行加减操作
- 应用于对象,先调用对象的 valueOf() 方法取得可操作的值,然后应用上述规则,若结果为 NaN,则调用 toString() 方法之后应用上述规则,对象变量变成数值变量
2. 一元加减操作符
一元加操作符置于非数字值之前时(置于之后会被认为是一个二元操作符),该操作符会像转型函数 Number() 一样对这个值执行转换。
一元减操作符主要用于表示负数,应用于非数字值时与一元加操作符类似。
位操作符
ECMAScript 中的所有数值都以 IEEE-754 64 位格式存储。位操作符不直接操作 64 位值,而是先将 64 位的值转换为 32 位的整数,然后执行操作,最后将结果转回 64 位。因此在实际操作过程中只存在 32 位整数。
对于有符号的整数,32 位中的前 31 位用于表示整数的值,第 32 位用于表示数值(符号位),0 表示正数,1 表示负数。
正负数都以二进制码存储,其中负数以其绝对值的二进制补码格式存储,计算补码的步骤如下:
- 求这个数值绝对值的二进制码
- 对二进制值求反码(0 换成 1,1 换成 0)
- 得到的反码加1
注意处理负数时,符号位是不能访问的。
把一个负数转换为二进制字符串,结果并不会显示补码,例如 (-6).toString(2) 的结果为"-0110",显然这种形式更符合逻辑。
默认情况下,ECMAScript 中所有整数都是有符号数。无符号数可以表示的值更大,因为多出一位来表示数值,但它不能表示负数。
在 ECMAScript 中,对数值应用位操作符时,后台自动将 64 位值转换为 32 位值,然后执行位操作,最后转回 64 位值。这会导致一个严重的问题:对 NaN 和 Infinity 值应用位操作符,它们会被当作 0 来处理。
在 ECMAScript 中,对非数值应用位操作符时,会先使用 Number() 函数转换为数值,再执行位操作,得到的结果是一个数值。
1. 按位非(NOT)
由一个波浪线(~)表示,执行结果是该数值的反码(负数以其绝对值的二进制补码格式存储,补码 = 反码 + 1)。
对 5(二进制:101)求反码,结果为 -6(二进制:1111 1111 1111 1111 1111 1111 1111 1010);
对 -5(二进制:1111 1111 1111 1111 1111 1111 1111 1011)求反码,结果为 4(二进制:100)。
可知按位非的本质就是:操作数的负值减 1。虽然通过算术式(-a-1)能得到相同结果,但是按位非的速度更快。
2. 按位与(AND)
由一个与字符(&)表示,它将两个数值按位对齐,只有两个对应位都是 1 时才返回 1,否则返回 0。例如求 25 & 3 :
25: 0000 0000 0000 0000 0000 0000 0001 1001
3: 0000 0000 0000 0000 0000 0000 0000 0011
&: 0000 0000 0000 0000 0000 0000 0000 0001
3. 按位或(OR)
由一个竖线(|)表示,它将两个数值按位对齐,只有两个对应位都是 0 时才返回 0,否则返回 1。例如求 25 | 3:
25: 0000 0000 0000 0000 0000 0000 0001 1001
3: 0000 0000 0000 0000 0000 0000 0000 0011
|: 0000 0000 0000 0000 0000 0000 0001 1011
4. 按位异或(XOR)
由一个插入符(^)表示,它将两个数值按位对齐,只有两个对应位值不同时才返回 1,否则返回 0。例如求 25 ^ 3:
25: 0000 0000 0000 0000 0000 0000 0001 1001
3: 0000 0000 0000 0000 0000 0000 0000 0011
^: 0000 0000 0000 0000 0000 0000 0001 1010
5. 左移
由两个小于号(<<)表示,它将数值所有位向左移动指定位数,低位补零。例如求 2 << 5:
2: 0000 0000 0000 0000 0000 0000 0000 0010
64: 0000 0000 0000 0000 0000 0000 0100 0000
通常,左移不会影响符号位,左移 n 位相当于乘以 2 的 n 次方。但如果左移位数过多,可能会覆盖符号位,使得符号位改变或者丢失高位的问题,例如 2 << 30 的结果是 -2147483648,2 << 31 的结果是 0。
6. 右移
分为有符号右移(或算术右移)和无符号右移(或逻辑右移)。
- 有符号右移
由两个大于号(>>)表示,它将数值所有位向右移动指定位数,符号位为1则高位补 1,否则补 0。 - 无符号右移
由 3 个大于号(>>>)表示,它将数值所有位向右移动指定位数,不考虑符号位,高位补 0。
由于负数是以其绝对值的二进制补码的格式表示,所以负数的无符号右移的结果会非常大。
布尔操作符
1. 逻辑非
由一个叹号(!)表示,可以应用于 ECMAScript 中的任何值,返回一个布尔值。逻辑非操作符会将操作的值转换为一个布尔值,取反并返回。转换规则如下:
- 对象,返回 false
- 空字符串,返回 true
- 非空字符串,返回 false
- 数值 0,返回 true
- 数值 NaN,返回 true
- 非 0 非 NaN 数值(包括 Infinity),返回 false
- null,返回 true
- undefined,返回 true
同时使用两个逻辑非操作符,可以将一个值转换为与其对应的布尔值,与 Boolean() 转型函数的结果相同。
2. 逻辑与
由两个和号(&&)表示,可以应用于 ECMAScript 中的任何值。JavaScript 首先将 && 两边的值转成 Boolean 类型,然后再算结果,返回 true 时取后面的值,否则取前面的值。
数值转换为 Boolean 类型 遵循以下规则:
- 操作数为 false、null、undefined、0 和 NaN、'',则返回 false
- 操作数为其他,则返回 true
逻辑与是短路操作,第一个数值得 false 就会直接返回第一个值,不再计算第二个。
3. 逻辑或
由两个竖线符号(||)表示,可以应用于 ECMAScript 中的任何值。JavaScript 首先将||两边的值转成 Boolean 类型,然后再算结果,返回 true 时取前面的值,否则取后面的值。
逻辑或也是短路操作,第一个数值得 true 就会直接返回第一个值。
乘性操作符
ECMAScript 定义了 3 个乘性操作符:乘法、除法、求模。
如果参与乘性计算的某个操作数不是数值,操作符会自动使用 Number() 转型函数执行类型转换。
1. 乘法
由一个星号(*)表示,处理特殊值的情况下,乘法操作符遵循下列规则:
- 如果有一个操作数是 NaN,则结果为 NaN
- Infinity 与 Infinity 相乘,则结果是 Infinity
- Infinity 与 0 相乘,则结果是 NaN
- Infinity 与非 0 数值相乘,则结果是 Infinity 或 -Infinity
- 如果有操作数不是数值,则调用 Number() 转换为数值,再应用上面的规则
2. 除法
由一个斜线号(/)表示,处理特殊值的情况下,除法操作符遵循下列规则:
- 如果有一个操作数是 NaN,则结果为 NaN
- Infinity 被 Infinity 除,则结果是 NaN
- Infinity 被 0 除,则结果是 Infinity
- Infinity 被非 0 有限数除,则结果是 Infinity 或 -Infinity
- 非 0 有限数被 Infinity 除,则结果是 0
- 非 0 有限数被 0 除,则结果是 Infinity 或 -Infinity
- 0 被 Infinity 除,则结果是 0
- 0 被 0 除,则结果是 NaN
- 如果有操作数不是数值,则调用 Number() 转换为数值,再应用上面的规则
3. 求模
由一个百分号(%)表示,处理特殊值的情况下,求模操作符遵循下列规则:
- 如果有一个操作数是 NaN,则结果为 NaN
- Infinity 对任意数取模,则结果是 NaN
- 任意数对 0 求模,则结果是 NaN
- 有限数(包括 0)对 Infinity 求模,则结果是该有限数本身
- 如果有操作数不是数值,则调用 Number() 转换为数值,再应用上面的规则
加性操作符
1. 加法
由一个加号(+)表示,处理特殊值的情况下,加法操作符遵循下列规则:
- 如果两个操作数都是字符串,则将其拼接起来
- 如果只有一个操作数是字符串,则调用另一个操作数的相应方法(对象,数值和布尔值调用其 toString() 方法,undefined 和 null 调用 String() 方法)取得其字符串,然后拼接起来
- 如果有一个操作数是 NaN,且另一个操作数不是字符串,则结果为 NaN
- Infinity 加 Infinity,则结果是 Infinity
- -Infinity 加 -Infinity,则结果是 -Infinity
- Infinity 加 -Infinity,则结果是 NaN
- -0 加 -0,则结果是 -0
- 0 加 -0 或者 -0 加 0,则结果是 0
- 如果有一个操作数是对象,则调用其 toString() 方法取得对应的字符串,然后拼接起来;如果该对象的 prototype 链中都没有实现自己的 toString() 的话,就会调用 Object.prototype.toString.call(该对象) 获取对应字符串,例如两个空白对象相加的结果是
"[object Object][object Object]"
(这里第一个 object 表示其数据类型,与 typeof() 功能一样,第二个 Object 表示其构造方法) - 如果有一个操作数是布尔值,undefined 或 null,则调用 Number() 方法取得对应的数值,然后相加
2. 减法
由一个减号(-)表示,处理特殊值的情况下,减法操作符遵循下列规则:
- 如果有一个操作数是 NaN,另一个操作数不是字符串,则结果为 NaN
- Infinity 减 Infinity,则结果是 NaN
- -Infinity 减 -Infinity,则结果是 NaN
- Infinity 减 -Infinity,则结果是 Infinity
- -Infinity 减 Infinity,则结果是 -Infinity
- 0 减 0,则结果是 0
- 0 减 -0,则结果是 0
- -0 减 0,则结果是 -0
- -0 减 -0,则结果是 0
- 如果有一个操作数是字符串、布尔值、undefined 或 null,则调用 Number() 函数将其转换为数值
- 如果有一个操作数是对象,则调用其 valueOf() 方法以获取该对象的数值,如果没有 valueOf() 方法则调用其 toString() 方法,并将得到的字符串通过 Number 转换为数值
关系操作符
由小于(<)、大于(>)、小于等于(<=)和大于等于(>=)组成,处理特殊值的情况下,关系操作符遵循下列规则:
- 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值(从第一个字符开始比较,如果相同则比较下一个,可以通过调用该字符串的 charCodeAt() 方法获取其编码值)
- 如果有一个操作数是数值,则将另一个操作数转换为数值(转换失败时直接返回 NaN,任何值与NaN比较结果都为 false),然后进行比较
- 如果有一个操作数是对象,则调用其 valueOf() 方法以获取对应值,再按照前面的规则进行比较,如果没有 valueOf() 方法则调用其 toString() 方法
- 如果有一个操作数是布尔值,则先将其转换为数值,然后进行比较
相等操作符
ECMAScript 提供两组操作符:相等与不相等(先转换为相似类型再比较),和全等与不全等(仅比较不进行转换)
1. 相等和不相等
相等操作符由两个等于号(==)表示,不相等操作符由(!=)表示,比较前都会先转换操作数(强制转换),转化时遵循下列基本规则:
- null 或 undefined 不会被转换,直接进行比较
- 布尔值被转换为数值(true 为 1,false 为 0),然后进行比较
- 如果有一个操作数是字符串,另一个是数值,则先将字符串转换为数值,再进行比较
- 如果有一个操作数是对象,而另一个不是对象,则调用对象的 valueOf() 方法,用得到的值进行比较
比较时遵循下列规则: - null 和 undefined 相等(它们也与自身相等,除此之外不与其他值相等)
- 如果有一个操作数是 NaN,则判断其不相等(两个操作数都是 NaN 结果也是不相等,因为 NaN 不等于 NaN)
- 如果两个操作数都是对象,则比较它们是否为同一个对象
2. 全等和不全等
全等操作符由 3 个等于号(=)表示,不全等操作符由(!)表示,全等没有转换步骤,直接进行比较
null 和 undefined 不全等
为了保持代码中数据类型完整性,推荐使用全等操作符
条件操作符
遵循与 Java 中条件操作符相同的语法形式:
条件 ? 返回值1 : 返回值2
条件的结果为 true 则得到返回值 1,否则得到返回值 2
赋值操作符
由等号表示,作用就是把右侧的值赋给左侧的值
在等号前面加上其他算术操作符,可以组成复合赋值操作符:
a *= b
相当于 a = a * b
/=、%=、+=、-= 与之类似
- 左移赋值:<<=(相当于乘以 2 的n次方,注意 JavaScript 的数字是 64 位有符号数,左移可能改变符号位)
- 右移赋值:
>>=
(相当于除以 2 的 n 次方,左边补上符号位) - 无符号右移赋值:
>>>=
(左边始终补 0)
复合运算符不会带来性能上的提升
逗号操作符
用于在一条语句中执行多个操作,声明多个变量。
此外还可用于赋值,如:
var a = (1,2,3); //此时逗号操作符只会返回最后一项