为啥 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】:
要为其他答案添加一点细节...当比较 Array
和 Number
时,Javascript 会将 Array
转换为 parseFloat(array)
。您可以在控制台(例如 Firebug 或 Web Inspector)中自己尝试一下,看看Array
的不同值会被转换成什么。
parseFloat([2]); // 2
parseFloat([2, 3]); // 2
parseFloat(['', 2]); // NaN
对于Array
s,parseFloat
对Array
的第一个成员执行操作,并丢弃其余成员。
编辑:根据 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"+3 在 JavaScript 中计算为 6? [复制]
为啥 [1,2] + [3,4] = "1,23,4" 在 JavaScript 中?
为啥 2 && 3 结果为 3 (javascript)? [复制]