第四章:强制转换 - 抽象关系比较

优质
小牛编辑
147浏览
2023-12-01

抽象关系比较

虽然这部分的 隐含 强制转换经常不为人所注意,但无论如何考虑比较a < b时发生了什么是很重要的(和我们如何深入检视a == b类似)。

在ES5语言规范的11.8.5部分的“抽象关系型比较”算法,实质上把自己分成了两个部分:如果比较涉及两个string值要做什么(后半部分),和除此之外的其他值要做什么(前半部分)。

注意: 这个算法仅仅定义了a < b。所以,a > b作为b < a处理。

这个算法首先在两个值上调用ToPrimitive强制转换,如果两个调用的返回值之一不是string,那么就使用ToNumber操作规则将这两个值强制转换为number值,并进行数字的比较。

举例来说:

  1. var a = [ 42 ];
  2. var b = [ "43" ];
  3. a < b; // true
  4. b < a; // false

注意: 早先讨论的关于-0NaN==算法中的类似注意事项也适用于这里。

然而,如果<比较的两个值都是string的话,就会在字符上进行简单的字典顺序(自然的字母顺序)比较:

  1. var a = [ "42" ];
  2. var b = [ "043" ];
  3. a < b; // false

ab 不会 被强制转换为number,因为它们会在两个arrayToPrimitive强制转换后成为string。所以,"42"将会与"043"一个字符一个字符地进行比较,从第一个字符开始,分别是"4""0"。因为"0"在字典顺序上 小于 "4",所以这个比较返回false

完全相同的行为和推理也适用于:

  1. var a = [ 4, 2 ];
  2. var b = [ 0, 4, 3 ];
  3. a < b; // false

这里,a变成了"4,2"b变成了"0,4,3",而字典顺序比较和前一个代码段一模一样。

那么这个怎么样:

  1. var a = { b: 42 };
  2. var b = { b: 43 };
  3. a < b; // ??

a < b也是false,因为a变成了[object Object]b变成了[object Object],所以明显地a在字典顺序上不小于b

但奇怪的是:

  1. var a = { b: 42 };
  2. var b = { b: 43 };
  3. a < b; // false
  4. a == b; // false
  5. a > b; // false
  6. a <= b; // true
  7. a >= b; // true

为什么a == b不是true?它们是相同的string值("[object Object]"),所以看起来它们应当相等,对吧?不。回忆一下前面关于==如何与object引用进行工作的讨论。

那么为什么a <= ba >= b的结果为true,如果a < ba == ba > b都是false

因为语言规范说,对于a <= b,它实际上首先对b < a求值,然后反转那个结果。因为b < a也是false,所以a <= b的结果为true

到目前为止你解释<=在做什么的方式可能是:“小于 等于”。而这可能完全相反,JS更准确地将<=考虑为“不大于”(!(a > b),JS将它作为(!b < a))。另外,a >= b被解释为它首先被考虑为b <= a,然后实施相同的推理。

不幸的是,没有像等价那样的“严格的关系型比较”。换句话说,没有办法防止a < b这样的关系型比较发生 隐含的 强制转换,除非在进行比较之前就明确地确保ab是同种类型。

使用与我们早先=====合理性检查的讨论相同的推理方法。如果强制转换有帮助并且合理安全,比如比较42 < "43"就使用它。另一方面,如果你需要在关系型比较上获得安全性,那么在使用<(或>)之前,就首先 明确地强制转换 这些值。

  1. var a = [ 42 ];
  2. var b = "043";
  3. a < b; // false -- 字符串比较!
  4. Number( a ) < Number( b ); // true -- 数字比较!