在 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") === true
是 true
),所以 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。
转换为数字的空字符串为0
(Number("") == 0
为true
)。
此时,比较变成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 行。发生了啥事?