抽象关系比较
虽然这部分的 隐含 强制转换经常不为人所注意,但无论如何考虑比较a < b
时发生了什么是很重要的(和我们如何深入检视a == b
类似)。
在ES5语言规范的11.8.5部分的“抽象关系型比较”算法,实质上把自己分成了两个部分:如果比较涉及两个string
值要做什么(后半部分),和除此之外的其他值要做什么(前半部分)。
注意: 这个算法仅仅定义了a < b
。所以,a > b
作为b < a
处理。
这个算法首先在两个值上调用ToPrimitive
强制转换,如果两个调用的返回值之一不是string
,那么就使用ToNumber
操作规则将这两个值强制转换为number
值,并进行数字的比较。
举例来说:
var a = [ 42 ];
var b = [ "43" ];
a < b; // true
b < a; // false
注意: 早先讨论的关于-0
和NaN
在==
算法中的类似注意事项也适用于这里。
然而,如果<
比较的两个值都是string
的话,就会在字符上进行简单的字典顺序(自然的字母顺序)比较:
var a = [ "42" ];
var b = [ "043" ];
a < b; // false
a
和b
不会 被强制转换为number
,因为它们会在两个array
的ToPrimitive
强制转换后成为string
。所以,"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; // ??
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
不是true
?它们是相同的string
值("[object Object]"
),所以看起来它们应当相等,对吧?不。回忆一下前面关于==
如何与object
引用进行工作的讨论。
那么为什么a <= b
和a >= b
的结果为true
,如果a < b
和a == b
和a > b
都是false
?
因为语言规范说,对于a <= b
,它实际上首先对b < a
求值,然后反转那个结果。因为b < a
也是false
,所以a <= b
的结果为true
。
到目前为止你解释<=
在做什么的方式可能是:“小于 或 等于”。而这可能完全相反,JS更准确地将<=
考虑为“不大于”(!(a > b)
,JS将它作为(!b < a)
)。另外,a >= b
被解释为它首先被考虑为b <= a
,然后实施相同的推理。
不幸的是,没有像等价那样的“严格的关系型比较”。换句话说,没有办法防止a < b
这样的关系型比较发生 隐含的 强制转换,除非在进行比较之前就明确地确保a
和b
是同种类型。
使用与我们早先==
与===
合理性检查的讨论相同的推理方法。如果强制转换有帮助并且合理安全,比如比较42 < "43"
,就使用它。另一方面,如果你需要在关系型比较上获得安全性,那么在使用<
(或>
)之前,就首先 明确地强制转换 这些值。
var a = [ 42 ];
var b = "043";
a < b; // false -- 字符串比较!
Number( a ) < Number( b ); // true -- 数字比较!