在 2 个空数组的这个松散相等比较中发生了啥

Posted

技术标签:

【中文标题】在 2 个空数组的这个松散相等比较中发生了啥【英文标题】:What is happening in this loose equality comparison of 2 empty arrays在 2 个空数组的这个松散相等比较中发生了什么 【发布时间】:2018-05-17 06:41:56 【问题描述】:

我正在努力理解这个 sn-p 在基本层面上的工作原理

if([] == ![])
console.log("this evaluates to true");

请帮助我理解我哪里弄错了。我的想法:

    首先存在运算符优先级,因此 !== 之前计算。 接下来调用ToPrimitive 并且[] 转换为空字符串。 ! 运算符注意到它需要将 "" 转换为 boolean,因此它采用该值并将其转换为 false,然后取反为 true== 更喜欢比较数字,所以在我看来 true 使 1[] 转换为 "" 然后 0

为什么它会起作用?我哪里弄错了?

【问题讨论】:

![]false,而不是 true,因为 [] 是一个对象。 ToPrimitive 不会在否定之前调用,它会在否定点调用,如果有的话。 == 强制第一个数组。 yes ![] 是假的,但它是如何工作的?为什么它是假的?这些才是真正的问题。空数组是对象的解释并不能说服我很抱歉 @KonradAlbrecht,但这就是解释。如果您对此不满意,那么这确实是您的问题。在 javascript 中,all 对象(除了null 不是真正的对象和document.all,这是一个完全不同的故事)是真实的。没什么好理解的,就是a given。数组是对象,所以[] 是真实的。 【参考方案1】:

为什么它会起作用?

TLDR:

[] == ![]
        //toBoolean [1]
[] == !true
[] == false
//loose equality round one [2]
//toPrimitive([]); toNumber(false) [3]
"" == 0
//loose equality round two
//toNumber("") [4]
0 === 0
true

一些解释:

1)

首先存在运算符优先级,因此!== 之前进行评估

Negating something 首先将内部toBoolean 方法调用到那个“某物”上。在这种情况下,这是一个对象(因为数组是对象),因此它总是返回 true,然后将其取反。

2)

现在取决于loose equalities special behaviour(有关更多信息,请参阅 Taurus 答案):

如果 A 是一个对象(数组是对象)并且 B 是一个布尔值,它会这样做:

ToPrimitive(A) == ToNumber(B)

3)

  toPrimitive([])

ToPrimitive(A) 尝试将其 Object 参数转换为原始值,方法是尝试在 A 上调用不同的 A.toString 和 A.valueOf 方法序列。

通过调用toString(因为它们没有valueOf 方法)将数组转换为其原语,这基本上是join(",")

toNumber(false)

如果参数为真,则结果为 1。如果参数为假,则结果为 +0。 Reference

所以false 被转换为+0

4)

toNumber("")

为空或仅包含空格的 StringNumericLiteral 将转换为 +0。

所以最后"" 被转换为+0

我哪里弄错了?

在第 1 步。否定某事不会调用toPrimitive,而是调用toBoolean ...

【讨论】:

ToPrimitive不参与解析![]吗? @David Hedlund nope only toBoolean 运算符 "==" 更喜欢比较数字。这意味着在第 3 步之后,两个值都转换为 0,然后进行比较? 确实,正如@KonradAlbrecht 所说,作为松散相等运算符== 的操作数的每个布尔值都必须强制转换为一个数字(请参阅我的答案)。除了这个细节,很好的答案。 @taurus 哦不!看起来我在表中引用了错误的行:/【参考方案2】:

接受的答案不正确(但现在是),请参阅此示例:

if([5] == true) 
console.log("hello");	

如果确实按照接受的答案状态处理了所有内容,则 [5] == true 应该评估为 true,因为数组 [5] 将被转换为其对应的字符串 ("5"),而字符串 "5"是真实的(Boolean("5") === truetrue),所以 true == true 必须是真实的。

但这显然不是这种情况,因为条件不计算为true所以,实际发生的是: 1. ![] 会将其操作数转换为布尔值,然后翻转该布尔值,每个对象都是真实的,因此 ![] 将评估为 false

此时,比较变成[] == false 2. 然后起作用的是这 2 条规则,clearly stated 在抽象等式比较算法规范中的 #6 中:

    如果 Type(x) 是布尔型,则返回比较结果 ToNumber(x) == y。 如果 Type(y) 是布尔值,则返回比较结果 x == ToNumber(y)

此时,比较变成[] == 0 3. 那么,就是这条规则:

如果 Type(x) 是 Object 并且 Type(y) 是 String 或 Number, 返回比较结果 ToPrimitive(x) == y。

正如@Jonas_W 所说,数组的ToPrimitive 将调用它的toString,这将返回其内容的逗号分隔列表(我过于简单化了)。

此时,比较变成"" == 0 4. 最后(嗯,差不多),这条规则:

如果 Type(x) 是 String 并且 Type(y) 是 Number, 返回比较结果 ToNumber(x) == y。

转换为数字的空字符串为0Number("") == 0true)。

此时,比较变成0 == 0 5. 最后,这条规则将适用:

如果 Type(x) 与 Type(y) 相同,那么 ………… 如果 Type(x) 是 Number,那么 ………… 如果 x 与 y 的数值相同,则返回 true。


这就是比较结果为true 的原因。您也可以将这些规则应用到我的第一个示例中,看看为什么它不能评估为 true

我上面引用的所有规则都在规范中明确说明了here。

【讨论】:

【参考方案3】:

首先,要意识到您正在比较两种不同类型的值。 当您使用 ![] 时,它会导致错误。你有代码(核心是这样):

if([] == false)

现在是第二部分:由于两个值的类型不同并且您使用的是“==”,因此 javascript 将两个值类型转换为字符串(以确保它们具有相同的类型)。它从左边开始。 在左边我们有 []。所以javascript在[]上应用toString函数,结果为假。试试console.log([].toString())

你应该在左边得到 false 作为字符串值。在右边我们有布尔值 false,javascript 用它做同样的事情。 它也在 false 上使用 toString 函数,这导致字符串值为 false。 试试console.log(false.toString())

这涉及两个核心概念:“==”如何工作以及“==”运算符的关联性——它是从左开始还是从右开始。

关联性是指调用什么运算符函数:从左到右或从右到左。 == 或 ! 等运算符或 + 是使用前缀符号的函数! 如果使用“===”,上面的 if 语句将导致 false。 我希望这会有所帮助。

【讨论】:

能否为您的主张提供参考? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 这是 mozila 开发者的两个链接,这是一个著名的网站。我在理解 js 课程的奇怪部分时学到了这些东西。 我的意思是javascript converts both value type into string So javascript applies the toString function on [], which results in false 我提供的第一个链接对此进行了讨论,如果您进行更多搜索,您会发现我在说的更详细。

以上是关于在 2 个空数组的这个松散相等比较中发生了啥的主要内容,如果未能解决你的问题,请参考以下文章

Laravel Fortify,Vuex。数组到字符串的转换错误。 FortifyServiceProvider.php 第 40 行。发生了啥事?

来瞧瞧希尔排序都做了啥

比较 NumPy 数组以便 NaN 比较相等

如何检查数组中的数字是不是相等?

java中如何比较两个数组确定是不是相等?

判断两个数组是不是相等