ES5 规范 11.8.5 节定义了“抽象关系比较”(abstract relational comparison),分为两个部分:比较双方都是字符串(后半部分)和其他情况(前半部分)。
比较双方首先调用 ToPrimitive
,如果结果出现非字符串,就根据 ToNumber
规则将双方强制类型转换为数字来进行比较。
var a = [ 42 ];
var b = [ "43" ];
a < b; // true
b < a; // false
如果比较双方都是字符串,则按字母顺序来进行比较:
var a = [ "42" ];
var b = [ "043" ];
a < b; // false
a
和 b
并没有被转换为数字,因为 ToPrimitive
返回的是字符串,所以这里比较的是 "42"
和 "043"
两个字符串,它们分别以 "4"
和 "0"
开头。因为 "0"
在字母顺序上小于 "4"
,所以最后结果为 false
。同理:
var a = [ 4, 2 ];
var b = [ 0, 4, 3 ];
a < b; // false
a
转换为 "4, 2"
,b
转换为 "0, 4, 3"
,同样是按字母顺序进行比较。再比如:
var a = { b: 42 };
var b = { b: 43 };
a < b; // false
因为a是[object Object]
,b也是[object Object]
,所以按照字母顺序a < b
并不成立。
下面一些奇怪的例子:
var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true
因为根据规范a <= b
被处理为b < a
,然后将结果反转。因为b < a
的结果是false
,所以 a <= b
的结果是 true
。
js中 <=
是“不大于”的意思(即 !(a > b)
,处理为 !(b < a)
)。同理 a >= b
处理为 b <= a
。
相等比较有严格相等,关系比较却没有“严格关系比较”(strict relational comparison)。也就是说如果要避免a < b
中发生隐式强制类型转换,我们只能确保a
和b
为相同的类型,除此之外别无他法。
与 ==
和 ===
的完整性检查一样,我们应该在必要和安全的情况下使用强制类型转换,如: 42 < "43"
。换句话说就是为了保证安全,应该对关系比较中的值进行显式强制类型转换:
var a = [ 42 ];
var b = "043";
a < b; // false -- 字符串比较!
Number( a ) < Number( b ); // true -- 数字比较!