为什么JavaScript中有2 == [2]?

时间:2009-11-12 18:10:18

标签: javascript arrays properties

我最近在JavaScript中发现了2 == [2]。事实证明,这个怪癖有几个有趣的后果:

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

同样,以下工作:

var a = { "abc" : 1 };
a[["abc"]] === a["abc"]; // this is also true

更奇怪的是,这也有效:

[[[[[[[2]]]]]]] == 2; // this is true too! WTF?

这些行为似乎在所有浏览器中都是一致的。

知道为什么这是一种语言功能吗?

以下是这个“功能”的更为疯狂的后果:

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

var a = [0];
a == a // true
a == !a // also true, WTF?

这些例子是由jimbojw http://jimbojw.com以及walkingeyerobot发现的。

9 个答案:

答案 0 :(得分:133)

您可以在ECMA规范中查找比较算法(ECMA-262的相关部分,第3版针对您的问题:11.9.3,9.1,8.6.2.6)。

如果将所涉及的抽象算法转换回JS,那么在评估2 == [2]时会发生什么呢?

2 === Number([2].valueOf().toString())

其中数组valueOf()返回数组本身,单元素数组的字符串表示形式是单个元素的字符串表示形式。

这也解释了第三个例子,因为[[[[[[[2]]]]]]].toString()仍然只是字符串2

正如你所看到的,涉及到很多幕后魔法,这就是我通常只使用严格相等运算符===的原因。

第一个和第二个例子更容易理解,因为属性名称总是字符串,所以

a[[2]]

相当于

a[[2].toString()]

只是

a["2"]

请记住,在任何数组魔法发生之前,即使数字键也被视为属性名称(即字符串)。

答案 1 :(得分:10)

这是因为==运算符的隐式类型转换。

与数字比较时,

[2]转换为数字2。在[2]上尝试一元+运算符。

> +[2]
2

答案 2 :(得分:10)

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true

在等式的右边,我们有a [2],它返回一个值为2的数字类型。在左边,我们首先创建一个单个对象为2的新数组。然后我们调用a [(数组在这里)]。我不确定这是评估字符串还是数字。 2,或“2”。让我们首先采取字符串案例。我相信一个[“2”]会创建一个新变量并返回null。 null!== 2.所以我们假设它实际上是隐式转换为数字。 a [2]会返回2. 2和2匹配类型(所以===有效)和值。我认为它隐式地将数组转换为数字,因为[value]需要一个字符串或数字。看起来数字的优先级更高。

在旁注中,我想知道谁确定了优先权。是因为[2]有一个数字作为它的第一个项目,所以它转换为一个数字?或者是当将数组传递到[数组]时,它会尝试先将数组转换为数字,然后再转换为数组。谁知道?

var a = { "abc" : 1 };
a[["abc"]] === a["abc"];

在此示例中,您将创建一个名为a的对象,其成员名为abc。等式的右边非常简单;它相当于a.abc。返回1.左侧首先创​​建一个[“abc”]的文字数组。然后,通过传入新创建的数组,在对象上搜索变量。由于这需要一个字符串,因此它将数组转换为字符串。这现在评估为[“abc”],等于1.1和1是相同的类型(这就是为什么===有效)和相等的值。

[[[[[[[2]]]]]]] == 2; 

这只是一种隐式转换。 ===在这种情况下不起作用,因为存在类型不匹配。

答案 3 :(得分:8)

对于==案例,这就是Doug Crockford建议始终使用===的原因。它不进行任何隐式类型转换。

对于===的示例,隐式类型转换在调用相等运算符之前完成。

答案 4 :(得分:7)

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

这很有趣,并不是[0]既是真的又是假的,实际上

[0] == true // false

这是javascript处理if()运算符的有趣方式。

答案 5 :(得分:6)

可以将一个项目的数组视为项目本身。

这是因为鸭子打字。由于“2”== 2 == [2]而且可能更多。

答案 6 :(得分:3)

要向其他答案添加一些细节...在将ArrayNumber进行比较时,Javascript会将Array转换为parseFloat(array)。您可以在控制台中自行尝试(例如Firebug或Web Inspector),以查看转换为不同Array值的内容。

parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN

对于ArrayparseFloatArray的第一个成员执行操作,并丢弃其余成员。

编辑:根据Christoph的详细信息,可能是内部使用较长的表单,但结果始终与parseFloat相同,因此您始终可以使用parseFloat(array)作为速记来确定它将如何转换。

答案 7 :(得分:2)

你在每种情况下比较两个对象..不要使用==,如果你想要比较,你在考虑===而不是==。 ==经常会产生疯狂的影响。寻找语言中的好部分:)

答案 8 :(得分:0)

问题的 EDIT 部分的说明:

第一个例子

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!

根据Christoph的回答,首先将类型[0]转换为原始值,我们得到“0”([0].valueOf().toString()

"0" == false

现在,对数字进行类型布尔(false),然后将字符串(“0”)强制转换为数字

Number("0") == Number(false)
or  0 == 0 
so, [0] == false  // true

对于if语句,如果if条件本身没有显式比较,则条件将评估 truthy 值。

只有 6个虚假值:false,null,undefined,0,NaN和空字符串“”。任何不是虚假价值的东西都是真正的价值。

由于[0]不是假值,它是一个真值,if语句的计算结果为真&执行声明。

第二个例子

var a = [0];
a == a // true
a == !a // also true, WTF?

再次输入将值转换为基元

    a = a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == "0" // true; same type, same value


a == !a
or  [0].valueOf().toString() == [0].valueOf().toString()
or  "0" == !"0"
or  "0" == false
or  Number("0") == Number(false)
or  0 = 0   // true