为啥 2 == [2] 在 JavaScript 中?

Posted

技术标签:

【中文标题】为啥 2 == [2] 在 JavaScript 中?【英文标题】:Why does 2 == [2] in JavaScript?为什么 2 == [2] 在 JavaScript 中? 【发布时间】:2010-12-16 00:08:28 【问题描述】:

我最近在 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?

【问题讨论】:

所以你发现幂等运算不仅在应用一次时会产生相同的输出,甚至更多。 【参考方案1】:

您可以在 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"]

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

【讨论】:

【参考方案2】:

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

[2] 与 Number 比较时转换为 Number is 2。在 [2] 上尝试一元 + 运算符。

> +[2]
2

【讨论】:

其他人说 [2] 被转换为字符串。 +"2" 也是数字 2。 其实没那么容易。 [2] 转换为字符串会更接近,但看看ecma-international.org/ecma-262/5.1/#sec-11.9.3【参考方案3】:
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] 有一个数字作为它的第一项,所以它转换为一个数字?还是当将数组传递给 a[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";字符串和数字之间的比较是通过转换字符串来完成的【参考方案4】:

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

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

【讨论】:

【参考方案5】:

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

这是由于鸭子打字。因为 "2" == 2 == [2] 甚至更多。

【讨论】:

因为它们的类型不匹配。在第一个示例中,首先评估左侧,它们最终在类型上匹配。 另外,我不认为duck-typing 是正确的词。更多的是与==运算符在比较之前执行的隐式类型转换有关。 这与鸭子类型无关,而是与弱类型有关,即隐式类型转换 Chetan 和 Christoph 所说的。【参考方案6】:
[0] == false // true
if ([0])  /* executes */  // [0] is both true and false!

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

[0] == true // false

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

【讨论】:

实际上,== 的工作方式很有趣;如果您使用实际的显式强制转换(即Boolean([0])!![0]),您会发现[0] 在布尔上下文中将评估为true:在JS 中,任何对象都被视为true 【参考方案7】:

要为其他答案添加一点细节...当比较 ArrayNumber 时,Javascript 会将 Array 转换为 parseFloat(array)。您可以在控制台(例如 Firebug 或 Web Inspector)中自己尝试一下,看看Array 的不同值会被转换成什么。

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

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

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

【讨论】:

【参考方案8】:

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

【讨论】:

【参考方案9】:

问题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 语句,如果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

【讨论】:

以上是关于为啥 2 == [2] 在 JavaScript 中?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 -1**2 在 JavaScript 中是语法错误?

为啥 1+ +"2"+3 在 JavaScript 中计算为 6? [复制]

为啥 [1,2] + [3,4] = "1,23,4" 在 JavaScript 中?

为啥 2 && 3 结果为 3 (javascript)? [复制]

为啥这个 javascript 代码有效并且在括号内使用逗号? [复制]

为啥会这样? (冒泡排序)[JavaScript]