为什么2==[2]在JavaScript中是空的?

为什么2==[2]在JavaScript中是空的?,javascript,arrays,properties,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; // thi

我最近在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?
这些行为似乎在所有浏览器中都是一致的

你知道为什么这是一种语言特征吗

以下是该“功能”更疯狂的后果:


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


这是因为duck类型。因为“2”==2==[2]甚至更多。

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

[2] 与数字比较时,转换为数字为2。请尝试[2]上的一元
+
运算符

> +[2]
2

对于
=
情况,这就是为什么建议始终使用
==
。它不做任何隐式类型转换

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

var a = [0, 1, 2, 3];
a[[2]] === a[2]; // this is true
在等式的右侧,我们有a[2],它返回一个值为2的数字类型。在左边,我们首先创建一个新数组,其中一个对象为2。然后我们调用一个[(数组在这里)]。我不确定它的计算结果是字符串还是数字。2或“2”。让我们先看字符串大小写。我相信[“2”]会创建一个新变量并返回null。空!==2.所以让我们假设它实际上隐式地转换为一个数字。a[2]将返回2。2和2在类型(so==works)和值上匹配。我认为它隐式地将数组转换为数字,因为[value]需要字符串或数字。看起来数字的优先级更高

顺便说一句,我想知道是谁决定了优先顺序。是因为[2]的第一项是数字,所以它会转换为数字吗?或者,当将数组传递到[array]时,它会尝试先将数组转换为数字,然后再转换为字符串。谁知道呢

var a = { "abc" : 1 };
a[["abc"]] === a["abc"];
在本例中,您正在创建一个名为a的对象,其中有一个名为abc的成员。等式的右边很简单;它相当于a.abc。这返回1。左侧首先创建[“abc”]的文字数组。然后通过传入新创建的数组来搜索对象上的变量。因为这需要一个字符串,所以它将数组转换为字符串。现在计算为a[“abc”],等于1。1和1是相同的类型(这就是为什么===有效)和相等的值

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

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

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

如果您将涉及的抽象算法翻译回JS,那么在计算
2==[2]
时会发生以下情况:

2 === Number([2].valueOf().toString())
其中,数组的
valueOf()
返回数组本身,一个元素数组的字符串表示形式是单个元素的字符串表示形式

这也解释了第三个示例为
[2][[2][[2][[2][2][3][]]。toString()
仍然只是字符串
2

正如你所看到的,有相当多的幕后魔法,这就是为什么我通常只使用严格的相等运算符
==

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

a[[2]]
相当于

a[[2].toString()]
这只是

a["2"]

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

要为其他答案添加一点细节。。。将
数组
数字
进行比较时,Javascript将
数组
转换为
parseFloat(数组)
。您可以在控制台中亲自尝试(例如Firebug或Web Inspector),以查看不同的
数组
值转换为什么

parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN
对于
Array
s,
parseFloat
Array
的第一个成员执行操作,并丢弃其余成员

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

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
这很有趣,事实上[0]并不是既正确又错误

[0] == true // false

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

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

对问题的编辑部分的解释:

第一个示例

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
var a = [0];
a == a // true
a == !a // also true, WTF?
根据上面Christoph的回答,第一次将[0]类型转换为原语值,我们得到了“0”(
[0].valueOf().toString()

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

Number("0") == Number(false)
or  0 == 0 
so, [0] == false  // true
对于
if
语句,如果if条件本身没有显式比较,则该条件将计算truthy

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

由于[0]不是假值,而是真值,
if
语句的计算结果为true并执行该语句


第二个示例

[0] == false // true
if ([0]) { /* executes */ } // [0] is both true and false!
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

因为他们的类型不匹配。在第一个例子中,首先计算左侧,最后它们在类型上匹配。另外,我认为duck type在这里不是正确的单词。在比较之前,它更多地与
==
运算符执行的隐式类型转换有关。这与duck类型无关,而是与弱类型有关,即Chetan和Christoph所说的隐式类型转换