1.关系操作符
小于、大于、小于等于和大于等于这几个关系操作符用于对两个值进行比较,返回一个布尔值。
1 | var result1 = 5 > 3; // true |
与 ES 中的其他操作符一样,当关系操作符的操作数使用了非数值时,也要进行数据转换或完成某些奇怪的操作。以下就是相应的规则:
- 如果两个操作数都是数值,则执行数值比较;
- 如果两个操作数都是字符串,则比较两个字符串对应的字符编码值;
- 如果一个操作数是数值,则将另一个操作数转换为一个数值,然后执行数值比较;
- 如果一个操作数是对象,则调用这个对象的
valueOf()
方法,用得到的结果按照前面的规则执行比较。如果对象没有valueOf()
方法,则调用toString()
方法,并用得到的结果根据前面的规则执行比较; - 如果一个操作数是布尔值,则先将其转换为数值,然后再执行比较。
在使用关系操作符比较两个字符串时,会执行一种奇怪的操作。很多人都会认为,在比较字符串值时,小于的意思是“在字母表中的位置靠前”,而大于则意味着“在字母表中的位置靠后”,但实际上完全不是那么回事。在比较字符串时,实际比较的是两个字符串中对应位置的每个字符的字符编码值。经过一番比较之后,再返回一个布尔值。由于大写字母的字符编码全部小于小写字母的字符编码,因此我们就会看到如下所示的奇怪现象:
1 | var result = "Brick" < "alphabet"; // true |
在这个例子中,字符串 "Brick"
被认为小于字符串 "alphabet"
。原因是字母 B 的字符编码是 66,而字母 a 的字符编码是 97。如果要真正按字母表顺序比较字符串,就必须把两个操作数转换为相同的大小写形式,然后再执行比较,如下所示:
1 | var result = "Brick".toLowerCase() < "alphabet".toLowerCase(); // false |
通过将两个操作数都转换为小写形式,就可以得出 "alphabet"
按字母表顺序排在 "Brick"
之前的正确判断了。
另一种奇怪的现象发生在比较两个数字字符串的情况下,比如下面这个例子:
1 | var result = "23" < "3"; // true |
确实,当比较字符串 "23"
是否小于 "3"
时,结果居然是 true
。这是因为两个操作数都是字符串,而字符串比较的是字符编码("2"
的字符编码是 50,而 "3"
的字符编码是 51)。不过,如果像下面例子中一样,将一个操作数改为数值,比较的结果就正常了:
1 | var result = "23" < 3; // false |
此时,字符串 "23"
会被转换为数值 23
,然后再与 3
进行比较,因此就会得到合理的结果。在比较数值和字符串时,字符串都会被转换为数值,然后再以数值方式与另一个数值比较。当然,这个规则对前面的例子是适用的。可是,如果那个字符串不能被转换成一个合理的数值呢?比如:
1 | var result = "a" < 3; // false, 因为 "a" 被转换成了 NaN |
由于字母 "a"
不能转换成合理的数值,因此就被转换成了 NaN
。根据规则,任何操作数与 NaN
进行关系比较,结果都是 false
。于是,就出现了下面这个现象:
1 | var result1 = NaN < 3; // false |
按照常理,如果一个值不小于另一个值,则一定是大于或等于那个值。然而,在于 NaN
进行比较时,这两个比较操作的结果都返回了 false
。
2.相等操作符
确定两个变量是否相等是编程中的一个非常重要的操作。在比较字符串、数值和布尔值的相等性时,问题还比较简单。但是在涉及到对象的比较时,问题就变得复杂了。最早的 ES 中的相等和不等操作符会在执行比较值钱,先将对象转换成相似的类型。后来,有人提出了这种转换到底是否合理的质疑。最后,ES 的解决方案就是提供两组操作符:相等和不相等——先转换再比较,全等和不全等——仅比较而不转换。
2.1 相等和不相等
ES 中的相等操作符由两个等于号(==
)表示,如果两个操作数相等,则返回 true
。而不相等操作符由叹号后跟等于号(!=
)表示,如果两个操作数不相等,则返回 true
。这两个操作符都会先转换操作数(通常称为强制转换),然后再比较它们的相等性。
在转换不同的数据类型时,相等和不相等操作符遵循下列基本规则:
- 如果有一个操作数是布尔值,则在比较相等性之前先将其转换为数值 ——
false
转换为0
,而true
转换为1
; - 如果一个操作数是字符串,另一个操作数是数值,在比较相等性之前先将字符串转换为数值;
- 如果一个操作数是对象,另一个操作数不是,则调用对象的的
valueOf()
方法,用得到的基本类型值按前面的规则进行比较;
这两个操作符在进行比较时要遵循下列规则:
null
和undefined
是相等的;- 要比较相等性之前,不能将
null
和undefined
转换成其他任何值; - 如果有一个操作数是
NaN
,则相等操作符返回false
,不相等操作符返回true
(即使两个操作数都是NaN
)。 - 如果两个操作数都是对象,则比较它们是不是同一个对象。如果两个操作数都指向同一个对象,则相等操作符返回
true
;否则,返回false
。
下表列出了一些特殊情况及比较结果:
表达式 | 值 |
---|---|
null == undefined | true |
“NaN” == NaN | false |
5 == NaN | false |
NaN == NaN | false |
NaN != NaN | true |
false == 0 | true |
true == 1 | true |
true == 2 | false |
undefined == 0 | false |
null == 0 | false |
“5” == 5 | true |
2.2 全等和不全等
除了在比较之前不转换操作数之外,全等和不全等操作符与相等和不相等操作符没有什么区别。全等操作符由 3 个等于号(===
)表示,它只在两个操作数未经转换就相等的情况下返回 true
,如下面例子所示:
1 | var result1 = ("55" == 55); // true,因为转换后相等 |
在这个例子中,第一个比较使用的是相等操作符比较字符串 "55"
和数值 55
,结果返回了 true
。如前所述,这是因为字符串 "55"
先被转换成了数值 55
,然后再与另一个数值 55
进行比较。第二个比较使用了全等操作符以不转换数值的方式比较同样的字符串和值。在不转换的情况下,字符串当然不等于数值,因此结果就是 false
。
不全等操作符由一个叹号后跟两个等于号(!==
)表示,它在两个操作数未经转换就不相等的情况下返回 true
。例如:
1 | var result1 = ("55" != 55); // false |
注意:null == undefined
会返回 true
,因为它们是类似的值;但 null === undefined
会返回 false
,因为它们是不同类型的值。
由于相等和不相等操作符存在类型转换问题,而为了保持代码中数据类型的完整性,推荐使用全等和不全等操作符。
3.条件操作符
条件操作符是 ES 中最为灵活的一种操作符,而且它遵循了与 Java 中的条件操作符相同的语法形式,如下面的例子所示:
1 | variable = boolean_expression ? true_value : false_value; |
本质上,这行代码的含义就是基于对 boolean_expression
求值的结果,决定给变量 variable
赋什么值。如果求值结果为 true
,则给变量 variable
赋 true_value
值;如果求值结果为 false
,则给变量 variable
赋 false_value
值。看下面的例子:
1 | var max = (num1 > num2) ? num1 : num2; |
这个例子中,max
将会保存一个最大的值。这个表达式的意思是:如果 num1
大于 num2
(关系表达式返回 true
),则将 num1
的值赋给 max
;如果 num1
小于或等于 num2
(关系表达式返回 false
),则将 num2
的值赋给 max
。
4.赋值操作符
简单的赋值操作符由等于号表示,其作用就是把右侧的值赋给左侧的变量,如下面的例子所示:
1 | var num = 10; |
如果在等于号前面再添加乘性操作符、加性操作符或位操作符,就可以完成复合赋值操作。这种复合赋值操作相当于是对下面常规表达式的简写形式:
1 | var num = 10; |
其中第二行代码可以用一个复合赋值来代替:
1 | var num = 10; |
每个主要算数操作符(及个别其他操作符)都有对应的复合赋值操作符。这些操作符如下所示:
- 乘/赋值 (
*=
) - 除/赋值 (
/=
) - 模/赋值 (
%=
) - 加/赋值 (
+=
) - 减/赋值 (
-=
) - 左移/赋值 (
<<=
) - 有符号右移/赋值 (
>>=
) - 无符号右移/赋值 (
>>>=
)
设计这些操作符的主要目的就是简化赋值操作。使用它们不会带来任何性能的提升。
5.逗号操作符
使用逗号操作符可以在一条语句中执行多个操作,如下面的例子所示:
1 | var num1 = 1, num2 = 2, num3 = 3; |
逗号操作符多用于声明多个变量;但除此之外,逗号操作符还可以用于赋值。在用于赋值时,逗号操作符总会返回表达式中的最后一项,如下面的例子所示:
1 | var num = (5, 1, 4, 8, 0); // num 的值为 0 |
由于 0
是表达式的最后一项,因此 num
的值就是 0
。虽然逗号的这种使用方式并不常见,但这个例子可以帮我们理解逗号的这种行为。