加法运算符是在运行时决定,到底是执行相加,还是执行连接。也就是说,运算子的不同,导致了不同的语法行为,这种现象称为“重载”(overload)。由于加法运算符存在重载,可能执行两种运算,使用的时候必须很小心。
'3' + 4 + 5 // "345"
3 + 4 + '5' // "75"
上面代码中,由于从左到右的运算次序,字符串的位置不同会导致不同的结果。
除了加法运算符,其他算术运算符(比如减法、除法和乘法)都不会发生重载。它们的规则是:所有运算子一律转为数值,再进行相应的数学运算。
1 - '2' // -1
1 * '2' // 2
1 / '2' // 0.5
上面代码中,减法、除法和乘法运算符,都是将字符串自动转为数值,然后再运算。
自增和自减运算符有一个需要注意的地方,就是放在变量之后,会先返回变量操作前的值,再进行自增/自减操作;放在变量之前,会先进行自增/自减操作,再返回变量操作后的值。
var x = 1;
var y = 1;
x++ // 1
++y // 2
上面代码中,x
是先返回当前值,然后自增,所以得到1
;y
是先自增,然后返回新的值,所以得到2
。
指数运算符(**
)完成指数运算,前一个运算子是底数,后一个运算子是指数。
2 ** 4 // 16
注意,指数运算符是右结合,而不是左结合。即多个指数运算符连用时,先进行最右边的计算。
// 相当于 2 ** (3 ** 2)
2 ** 3 ** 2
// 512
上面代码中,由于指数运算符是右结合,所以先计算第二个指数运算符,而不是第一个。
布尔运算符用于将表达式转为布尔值,一共包含四个运算符。
- 取反运算符:
!
- 且运算符:
&&
- 或运算符:
||
- 三元运算符:
?:
且运算符(&&
)往往用于多个表达式的求值。
它的运算规则是:如果第一个运算子的布尔值为true
,则返回第二个运算子的值(注意是值,不是布尔值);如果第一个运算子的布尔值为false
,则直接返回第一个运算子的值,且不再对第二个运算子求值。
't' && '' // ""
't' && 'f' // "f"
't' && (1 + 2) // 3
'' && 'f' // ""
'' && '' // ""
var x = 1;
(1 - 1) && ( x += 1) // 0
x // 1
上面代码的最后一个例子,由于且运算符的第一个运算子的布尔值为false
,则直接返回它的值0
,而不再对第二个运算子求值,所以变量x
的值没变。
这种跳过第二个运算子的机制,被称为“短路”。有些程序员喜欢用它取代if
结构,比如下面是一段if
结构的代码,就可以用且运算符改写。
if (i) {
doSomething();
}
// 等价于
i && doSomething();
上面代码的两种写法是等价的,但是后一种不容易看出目的,也不容易除错,建议谨慎使用。
且运算符可以多个连用,这时返回第一个布尔值为false
的表达式的值。如果所有表达式的布尔值都为true
,则返回最后一个表达式的值。
true && 'foo' && '' && 4 && 'foo' && true
// ''
1 && 2 && 3
// 3
上面代码中,例一里面,第一个布尔值为false
的表达式为第三个表达式,所以得到一个空字符串。例二里面,所有表达式的布尔值都是true
,所以返回最后一个表达式的值3
。
或运算符(||
)也用于多个表达式的求值。它的运算规则是:如果第一个运算子的布尔值为true
,则返回第一个运算子的值,且不再对第二个运算子求值;如果第一个运算子的布尔值为false
,则返回第二个运算子的值。
't' || '' // "t"
't' || 'f' // "t"
'' || 'f' // "f"
'' || '' // ""
短路规则对这个运算符也适用。
var x = 1;
true || (x = 2) // true
x // 1
上面代码中,或运算符的第一个运算子为true
,所以直接返回true
,不再运行第二个运算子。所以,x
的值没有改变。这种只通过第一个表达式的值,控制是否运行第二个表达式的机制,就称为“短路”(short-cut)。
或运算符可以多个连用,这时返回第一个布尔值为true
的表达式的值。如果所有表达式都为false
,则返回最后一个表达式的值。
false || 0 || '' || 4 || 'foo' || true
// 4
false || 0 || ''
// ''
上面代码中,例一里面,第一个布尔值为true
的表达式是第四个表达式,所以得到数值4。例二里面,所有表达式的布尔值都为false
,所以返回最后一个表达式的值。