为什么将整数与长度为1的数组进行比较会返回true,而与长度为2或更多的数组进行比较会返回false?

10

为什么将0与长度为1的数组进行比较会返回true,而与长度大于2的数组进行比较会返回false?例如:

var a=[]  //undefined
0<a  //returns false
a.push(1) // [1]
0<a // returns true
a.push(2) // [1, 2]
0<a // return false
a.push(3) // [1, 2, 3]
0<a // return false
7个回答

9
基本上,您会得到一个隐式类型转换,首先是使用 toString 方法,

Array 对象覆盖了 ObjecttoString 方法。对于数组对象,toString 方法将数组连接起来,并返回一个包含由逗号分隔的每个数组元素的字符串。

当 JavaScript 要将数组表示为文本值或在字符串连接中引用数组时,自动调用 toString 方法。

就像使用 join 方法一样,然后将其转换为数字。

what you do   what you get  result  array        string      number
------------  ------------  ------  ---------    --------    ----------
var a = [];
0 < a         0 < 0         false   []        -> ''       -> 0
a.push(1);
0 < a         0 < 1         true    [1]       -> '1'      -> 1
a.push(2);
0 < a         0 < NaN       false   [1, 2]    -> '1,2'    -> NaN
a.push(3);
0 < a         0 < NaN       false   [1, 2, 3] -> '1,2,3'  -> NaN

1
你有没有一个参考资料表明在这种情况下JavaScript首先执行字符串转换,然后再进行数字转换? - trincot
@trincot,在ECMA 262 V6 [7.2.11抽象关系比较](https://www.ecma-international.org/ecma-262/6.0/#sec-abstract-relational-comparison)中,它首先尝试获取一个原始值,该值通过toString方法获得。 - Nina Scholz
@NinaScholz 注意其中的PreferredType,它似乎是一个数字。 - Pankaj Shukla
@PankajShukla,对的。转换为字符串后,您会得到数值。 - Nina Scholz
@NinaScholz 对我来说,首先转换为字符串似乎是不必要的,文献也似乎建议这样做。实际上,如果您删除字符串列,仍然会得到相同的结果。您的看法是什么? - Pankaj Shukla
显示剩余2条评论

4
比较数字和数组会触发类型转换。EcmaScript规范在7.1.3节中规定了此类转换的规则。根据这些规则,数字不会转换为另一种数据类型,但对象(包括数组)将被转换,方法如下:
  1. primValue为ToPrimitive(argument, hint Number)。
  2. 返回ToNumber(primValue)。
ToPrimitive函数在7.1.1节中有描述。

@@toPrimitive是一个符号,你可以通过Symbol.toPrimitive访问它。问题在于Array没有这个属性,所以进程继续到以下步骤:

  1. 返回OrdinaryToPrimitive(input,hint)。

当用参数O和hint调用抽象操作OrdinaryToPrimitive时,执行以下步骤:

  1. 如果hint为“string”,则
        a. 让methodNames为«"toString", "valueOf"»。
  2. 否则,
        a. 让methodNames为«"valueOf", "toString"»。

由于hint是“number”,我们处于第二种情况。接下来的步骤解释了按顺序应用这些方法。

现在,Array.prototype.valueOf只返回数组本身,因此由于类型是对象(即数组),所以以下子步骤将不会返回:

5.c.iii 如果 Type(result) 不是 Object,则返回 result

因此,备用方法启动,并在数组上调用 toString

因此,将数组转换为字符串。这是上面列出的两个步骤中的第一个步骤的结果:

  1. primValue 成为 ToPrimitive(argument, hint Number)。
  2. 返回 ToNumber(primValue)。

第二步更简单:然后将字符串转换为数字,这在 第7.1.3.1节 中有描述。

作为Array.prototype.toString方法创建逗号分隔列表,一旦数组有多个元素,这些字符串就会变成无效数字。如规范所述,返回值为NaN。任何与NaN的<比较都将返回false,这解释了您得到的输出结果。

1
如果您在上面的每一行之后将数组作为数字进行检查,就会得到您想要的答案。 请记住,在比较过程中,JavaScript 会执行一些类型转换。
Number(a); //insert this after each line

为什么 push(2)push(3) 返回 NaN - apires
console.log(a.toString(), Number(a.toString()) - epascarello

1
在像JS这样的弱类型语言中,您需要注意可变类型(如数组)如何强制转换为不可变类型。基本上,[1,2] 变成了 "1,2"
因此,[1,2] == "1,2" // <- true

这与一个值是否不可变没有真正的关系。即使使用 Object.freeze 使数组不可变,你仍然会得到这种强制转换。 - trincot
是的,你说得对。我的意思是类型强制转换是针对不可变类型的。 - Redu
我觉得你可能是指"原始类型",而不是那么"不可变"。还有一些不可变的对象,并且强制转换并不针对它们。 - trincot

0
在JS中,如果您的数组中只有一个元素,JS将计算该元素的值,例如:
var b[]
b.push(8);
b == 8 //true (but b === 8 is false!)

如果数组中有多个元素,则数组类型为array[number],这与number不同,因此您无法将其与数字进行比较。

这就是为什么在您的代码中执行以下操作后:

var a=[]  
0<a  
a.push(1) 
0<a 

你得到了true,因为a的值现在是“1”,大于0。但是,在下一次push之后,该值变成了[1,2],它不是一个数字(NaN),这就是为什么你得到了false。


0

就比较而言,JavaScript 通常会尝试将操作数转换为数字(优先考虑)。

看这个例子:

console.log("test" == true);//false
console.log("test" == false);//false

为什么?因为“test”试图被转换成数字,所以Number("test")会得到NaN,而NaN既不等于true也不等于false。

0<a becomes 0 < Number(a) becomes 0 < 0 returns "false".

作为一个特殊情况,当数组只有一个元素时,Number 能够将其强制转换为数字,但是当你添加多个元素时,应该返回什么数字呢? 在这种情况下,Number(arr) 变成了 NaN,而且 0 不小于 NaN。

0
如果你想比较数组的长度,请使用 a.length

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接