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


164

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

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发现

Answers:


134

您可以在ECMA规范中查找比较算法(有关问题的ECMA-262,第三版的相关部分: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"]

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



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

在方程式的右侧,我们有a [2],它返回值为2的数字类型。在左侧,我们首先创建一个具有单个对象2的新数组。然后调用a [(数组在这里)]。我不确定这是字符串还是数字。2,或“ 2”。让我们先考虑字符串大小写。我相信a [“ 2”]将创建一个新变量并返回null。null!==2。因此,假设它实际上是在隐式转换为数字。a [2]将返回2。2和2在类型和值上匹配(因此===有效)。我认为这是将数组隐式转换为数字,因为a [value]需要一个字符串或数字。看来数字的优先级更高。

附带说明一下,我想知道是谁确定了优先级。是因为[2]的第一项是数字,所以它转换为数字?还是在将数组传递到[array]时尝试将数组先转换为数字,然后转换为字符串。谁知道?

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

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

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

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


有关优先级的问题的答案:==适用ToPrimitive()于数组,数组继而调用其toString()方法,因此您实际比较的是2字符串的数字"2";字符串与数字之间的比较通过转换字符串来完成
Christoph

8

对于这种==情况,这就是为什么Doug Crockford建议始终使用的原因===。它不执行任何隐式类型转换。

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


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

很有意思,并不是[0]既是真是假

[0] == true // false

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


4
实际上,这是一种有趣的方式==。如果您使用实际的显式强制转换(即Boolean([0])!![0]),则会发现[0]它将true在布尔上下文中求值,如下所示:在JS中,将考虑任何对象true
Christoph

6

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

这是由于鸭子的打字。由于“ 2” == 2 == [2],甚至可能更多。


4
因为它们的类型不匹配。在第一个示例中,首先评估左侧,然后在类型上匹配它们。
肖恩

8
另外,我不认为在这里输入鸭子是正确的词。比较之前,它更多地与==运算符执行的隐式类型转换有关。
Chetan S

14
这与鸭子类型无关,而与弱类型无关,即隐式类型转换
Christoph

@Chetan:他说了什么;)
Christoph

2
凯坦和克里斯托夫说的话。
Tim Down

3

为了一个小细节添加到其他的答案...一个比较时ArrayNumber,使用Javascript将转换Array使用parseFloat(array)。您可以在控制台(例如Firebug或Web Inspector)中自己尝试一下,以查看将不同的Array值转换为哪些值。

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

对于ArrayparseFloat在的Array第一个成员上执行操作,然后丢弃其余成员。

编辑:根据Christoph的详细信息,可能是它在内部使用了较长的格式,但是结果始终与相同parseFloat,因此您始终可以将其parseFloat(array)用作简写形式,以确保将如何进行转换。


2

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


0

问题的EDIT部分的说明:

第一个例子

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

根据上述克里斯托夫的答案,第一个将类型[0]转换为原始值,我们得到“ 0”([0].valueOf().toString()

"0" == false

现在,将Boolean(false)转换为Number,然后将String(“ 0”)转换为Number

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

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

伪造的值只有6个:false,null,undefined,0,NaN和空字符串“”。任何不是虚假的价值就是真实的价值。

由于[0]不是虚假值,所以它是真实值,因此该if语句评估为true并执行该语句。


第二个例子

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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.