"Greater than"(和另一个不等式比较运算符)在数组上的行为

Behavior of "Greater than" (and another inequality comparison operators) on arrays

我找不到任何关于 ><<=>= 运算符在比较 [=24] 中的两个数组时的行为方式的任何描述或提及=].

我能想到的唯一微不足道的事情是两个数组的每个相对索引的两个元素进行比较,但在测试之后 - 我没有得到我预期的结果。

那么如何比较数组?

几个测试用例:

console.log([1] > [2]); // FALSE - ok
console.log([2] > [1]); // TRUE - ok
console.log([2] > [2]); // FALSE - ok
console.log([1,2] > [2,3]); // FALSE - In case two elements in each index are compared, and the answer is "If all >" - ok  
console.log([2,2] > [1,1]); // TRUE - In case two elements in each index are compared, and the answer is "If all >" - ok
console.log([1,2] > [1,1]); // TRUE - In case two elements in each index are compared, and the answer is "If all >" - Unexpected
// More cases with 3 elements:
console.log([1,2,1] > [1,2,2]); // FALSE
console.log([1,1,1] > [1,1,2]); // FALSE
console.log([1,3,1] > [1,3,0]); // TRUE
console.log([1,1,1] > [1,1,2]); // FALSE

通过调用不带参数的 Array#toString method before comparing, which acts the same as Array#join 将数组转换为字符串。

字符串比较的工作原理是按顺序比较两个字符串的每个字符的代码点。

要了解测试用例的结果,您需要了解使用任何关系运算符比较数组或任何其他对象时会发生什么。

长话短说,对象在使用任何关系运算符进行比较之前被转换为字符串

如果您不想阅读有关对象到基元的转换,请跳至答案末尾。)

ToPrimitive抽象操作

要将对象转换为原始值,javascript 执行 toPrimitive 带有两个参数的抽象操作:

  • input: 应转换为原始值的对象
  • preferredType:可选的第二个参数,指定将对象转换为原始值时应优先使用的类型

对于对象到基元的转换,toPrimitive 抽象操作调用另一个称为 OrdinaryToPrimitive

的抽象操作

OrdinaryToPrimitive 抽象操作

对于对象到基元的转换,toPrimitive 抽象操作调用 OrdinaryToPrimitive 带有两个参数的抽象操作:

  • O: 应转换为原始值的对象
  • hint: 将对象转换为原始值时应该优先使用的类型

toPrimitive 抽象操作设置 hint 如下:

  • 如果 preferredTypestring,请将 hint 设置为 string
  • 如果 preferredTypenumber,请将 hint 设置为 number
  • 如果未指定preferredType,则将hint设置为number

OrdinaryToPrimitive抽象操作使用以下三种算法将对象转换为原始值:

  • prefer-string: 如果 hintstring, return 原始值, 更喜欢字符串值, 如果可以转换为字符串

  • prefer-number: 如果 hintnumber, return 原始值, 更喜欢数字值, 如果可以转换为数字

  • no-preference: 该算法不表示应该 returned 什么类型的原始值的偏好,并让对象定义什么类型原始值的应该 returned。如果hintdefault或者不存在hint,这个算法用于将对象转换为原始值。

    它允许对象覆盖默认的 ToPrimitive 行为。在内置对象中,只有 DateSymbol 对象会覆盖默认的 ToPrimitive 行为。 DateSymbol 对象将此算法实现为 prefer-string 而所有其他内置对象将此算法实现为 prefer-number 对象可以覆盖默认 ToPrimitive 行为通过实现 Symbol.toPrimitive 方法 .)

所有对象都继承了两个用于将对象转换为原始值的方法。这两种方法是:

  • .valueOf()
  • .toString()

object to primitive conversion涉及调用上面提到的方法和上面提到的object to primitive conversion algorithm,调用这两个方法的顺序不同。

首选字符串

此算法首先调用 .toString() 方法,如果结果值是原始值,javascript 使用 returned 原始值,即使它不是字符串。

如果 .toString() 方法不存在或者它 return 是一个 object,则调用 .valueOf() 方法。如果 .valueOf() 方法 return 是原始值,则使用该值,否则 TypeError 被抛出。

首选号码

这个算法和prefer-string的唯一区别是它先调用.valueOf()方法,然后调用.toString()方法。

无偏好

当没有首选类型提示时,或者如果首选类型default,默认使用prefer-number算法。

对象可以覆盖此行为和所有内置对象,只有 DateSymbol 覆盖此默认 ToPrimitive 转换行为。 DateSymbol 在没有 首选类型 提示 首选类型为默认类型。


现在回到你的问题,关系运算符,即<, >=, <, <=可以用来比较字符串和数字。如果这些运算符的任一操作数是 object,则使用 prefer-number 算法将其转换为原始值。因此,当您使用关系运算符比较两个数组时,javascript 会尝试使用 prefer-number 算法将每个数组转换为原始值。

如上所述,prefer-number算法首先调用.valueOf()方法。如果 return 值是原始值,则使用该值,否则调用 .toString() 方法。

.valueOf() 方法的默认实现只是 return 对象本身而不是 return 原始值,所以 javascript 总是以调用 .toString() 方法,当它使用 prefer-number 算法时。

当在数组上调用 .toValue() 方法时,它只是 return 调用此方法的数组。 Javascript 然后在这个 returned 数组上调用 .toString() 方法。当在数组上调用 .toString() 方法时,它将数组的所有元素转换为字符串,然后将所有字符串连接在一起,每个字符串之间用逗号分隔。

因此,当您比较 [1] > [2] 时,您正在比较 '1' > '2' 并且类似地 [1,2] > [1,1] 被转换为 '1,2' > '1,1'

当字符串通过它们的 unicode 代码点进行比较时,'1' > '2' 计算为 false'1,2' > '1,1' 计算为 true