【JS红宝书¹⁴】关系操作符

336 阅读1分钟

引子

本文讲述为 ECMA-262 中描述可用于操作数据值的操作符中的 关系操作符

关系操作符

关系操作符执行比较两个值的操作,包括小于(<)、大于(>)、小于等于(<=)和大于等于(>=),用法跟数学课上学的一样;

常规写法

这几个操作符都返回布尔值,如下所示:

 let a = 10 > 3; // true
 let b = 22 < 2; // false

类型转换规则

与 ECMAScript 中的其他操作符一样,在将它们应用到不同数据类型时也会发生类型转换和其他行为:

  • 如果操作数都是数值,则执行数值比较
  • 如果操作数都是字符串,则逐个比较字符串中对应字符的编码
  • 如果有任一操作数是数值,则将另一个操作数转换为数值,执行数值比较
  • 如果有任一操作数是对象,则调用其 valueOf() 方法(如果没有则调用 toString() 方法),取得结果后再根据前面的规则执行比较
  • 如果有任一操作数是布尔值,则将其转换为数值再执行比较

小知识

在使用关系操作符比较两个字符串时,会发生一个有趣的现象:

很多人认为小于意味着“字母顺序靠前”,而大于意味着“字母顺序靠后”,实际上不是这么回事;对字符串而言,关系操作符会比较字符串中对应字符的编码,而这些编码是数值,比较完之后,会返回布尔值

🕳️问题1

大写字母的编码都小于小写字母的编码,因此以下这种情况就会发生:

 let result = "Brick" < "alphabet"; // true 

在这里,字符串"Brick"被认为小于字符串"alphabet",因为字母 B 的编码是 66,字母 a 的编码是 97。

🥚解决

要得到按字母顺序比较的结果,就必须把两者都转换为相同的大小写形式(全大写或全小写) ,然后再比较:

 let result = "Brick".toLowerCase() < "alphabet".toLowerCase(); // false 

将两个操作数都转换为小写,就能保证按照字母表顺序判定"alphabet""Brick"前头。

🕳️问题2

另一个奇怪的现象是在比较两个数值字符串的时候,比如下面这个例子:

 let result = "23" < "3"; // true 

因为两个操作数都是字符串,所以会逐个比较它们的字符编码(字符"2"的编码是 50,而字符"3"的编码是 51);

🥚解决

不过,如果有一个操作数是数值,那么比较的结果就对了:

 let result = "23" > 3; // true 

因为这次会将字符串"23"转换为数值 23,然后再跟 3 比较,结果当然对了。

只要是数值和字符串比较,字符串就会先被转换为数值,然后进行数值比较

🕳️问题3

对于数值字符串而言,这样能保证结果正确, 但如果字符串不能转换成数值呢? 如下这个例子:

 let result = "a" < 3; 
 // 因为"a"会转换为 NaN,所以结果是 false 

因为字符"a"不能转换成任何有意义的数值,所以只能转换为 NaN

这里有一个规则,任何关系操作符在涉及比较 NaN 时都返回 false

这样一来,下面的例子有趣了:

 let result1 = NaN < 3; // false 
 let result2 = NaN >= 3; // false 

在大多数比较的场景中,如果一个值不小于另一个值,那就一定大于或等于它;但 在比较 NaN 时,无论是小于还是大于等于,比较的结果都会返回 false

总结

关系操作符为执行比较两个值的操作;有四种操作符分别为:小于(<)、大于(>)、小于等于(<=)和大于等于(>=); 当操作数都是字符串比较编码;当操作数都是字符串比较编码数;当比较中有任一是数值,把另一个转换为数值;以及任何关系操作符在涉及比较 NaN 时都返回 false