ES5 规范 11.8.5 节定义了“抽象关系比较”(abstract relational comparison),分为两个部分:比较双方都是字符串(后半部分)和其他情况(前半部分)。
比较双方首先调用 ToPrimitive,如果结果出现非字符串,就根据 ToNumber 规则将双方强制类型转换为数字来进行比较。
var a = [ 42 ];var b = [ "43" ];a < b; // trueb < 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; // falsea == b; // falsea > b; // falsea <= b; // truea >= 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 -- 数字比较!
