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

我最近在 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?
19987 次浏览

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

这是由于输入了 Duck,因为“2”= = 2 = = [2]或者更多。

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

与数字相比,[2]被转换为 Number is 2。请尝试在[2]上使用一元 +运算符。

> +[2]
2

对于 ==的情况,这就是为什么 道格 · 克罗克福德总是推荐使用 ===。它不做任何隐式的类型转换。

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

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

在方程的右边,我们有 a [2] ,它返回一个值为2的数字类型。在左边,我们首先创建一个对象为2的新数组。然后我们调用一个[(array is in here)]。我不确定它的计算结果是字符串还是数字。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”]。然后通过传入新创建的数组在对象上搜索变量。由于这需要一个字符串,因此它将数组转换为一个字符串。现在计算结果为[“ abc”] ,等于1。1和1是相同的类型(这就是为什么 = = 可以工作)并且具有相同的值。

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

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

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

如果将相关的抽象算法转换回 JS,那么在评估 2 == [2]时会发生的基本情况是:

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

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

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

正如您所看到的,其中涉及到许多幕后魔术,这就是为什么我通常只使用严格相等操作符 ===的原因。

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

a[[2]]

相当于

a[[2].toString()]

也就是说

a["2"]

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

为了给其他答案添加一些细节... ... 当比较 ArrayNumber时,Javascript 将使用 parseFloat(array)转换 Array。您可以自己在控制台(例如 Firebug 或 Web 检查器)中尝试它,以查看不同的 Array值被转换为什么。

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

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

编辑: 根据 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!

根据上面 Christoph 的回答,首先将[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语句,如果 if 条件本身没有显式比较,则条件计算 真心话值。

只有 6个错误值: false、空、未定义、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