为什么在JavaScript中这个表达式的结果是"a"?

7

我得到了一些混淆的JavaScript代码。我试图理解它,为此我在控制台中输入了一些代码片段。但是我无法理解其中的原因。

> ((!!+[]+"")[+!![]])
< "a"

为什么在JavaScript中((!!+[]+"")[+!![]])等于"a"呢?有其他代码片段可以获得其他字母吗?
我猜这与自动类型转换有关。

4
这整个事情可以简化为(“false” [1])。现在你知道为什么结果是a了。 - abhishekkannojia
a是false的第二个字母。聪明^^ - WayToDoor
@abhishekkannojia 为什么 (!!+[]+"") 等于 "false",而 +!![] 等于 1 - Toan Tran
5个回答

8
( ( !!+[] + "" ) [ +!![] ] )
( (  !!0  + "" ) [ +true ] )
( ( false + "" ) [ +true ] )
( (   "false"  ) [   1   ] )
(         "false"[1]       )
(            "a"           ) 

有没有其他的代码片段可以获取其他字母? 您可以使用相同的概念来获取“true”、“false”、“undefined”、“NaN”等所有字母。

1
那是正确的答案。 - SVSchmidt
1
为什么 +[] 的值为0?会不会是将空数组转换成了数字,那么为什么不是0呢?这应该只是个小问题,如果可能的话最好能有更好的解释。 - Hugues M.
@HuguesMoreau 一元加操作符 +[] 解析为数字类型。 - pomber
为什么?https://www.destroyallsoftware.com/talks/wat - pomber

4
让我们使用控制台获取答案,假设我们不知道这些内容的意思,键入[] + ""在控制台中输出""。仅输入(!!+[])返回布尔值false。如果将布尔值false附加到""上,则由于类型转换,将得到字符串false。如预期地,键入(!!+[]+"")在控制台中输出"false"
接下来,在JavaScript中,您可以将字符串视为字符数组,并可以使用数组表示法访问其字符。因此,在((!!+[]+"")[+!![]])中,您可以去掉最外层括号,使它看起来更简单。现在我们有(!!+[]+"")[+!![]],其中括号中的第一部分返回字符串"false",而括号中的下一个部分[]则访问了字符串"false"的字符。现在您可以确定+!![]某种程度上返回1,因为"false"[1]等于"a"
现在让我们找出+!![]如何等于1[]是一个空数组,您可以将其视为0,在JavaScript中这将是true(因为“在JavaScript中任何“真实”的东西都是true”),因此![]false!![]true
现在我们只剩下+true,这只是将true转换为数字的简写,其结果是1。现在您可以看到+!![]如何计算出1,并且您了解(希望如此)这个混淆的代码如何工作!

4
你应该在JavaScript中处理运算符优先级和类型转换:
!!+[] // Is falsey. this is same for !!+0 or !!+""
false + "" // Is "false". as 5+"" is "5".

![] // Is falsey.
!false // Is true
+true //  Is equal to 1. +[] = 0, +false = 0

最后,至少需要:

"false"[1] // Is "a"

2
你有没有关于JavaScript运算符优先级的好参考资料? - Peter Mortensen
我相信Mozilla的文档足够好了。我总是去那里查阅资料。https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table - marmeladze

3
理解这一点的关键是要知道JavaScript对它看到的表达式进行隐式类型转换。换句话说,虽然您可能不知道将数字添加到字符串意味着什么,但JavaScript会猜测而不是发出错误。这与在此情况下会给出显式错误的C ++相反。
例如,无论x的类型是什么,+x始终计算为数字。对于!x也是如此。因此,对于您的表达式:
// A: !!+[]+"" which is evaluated like !(!(+[]))+""
+[]       === 0
!0        === true
!true     === false
false+''  === 'false'

// B: +!![] which is evaluated like +(!(![]))
![]       === false
!false    === true
+true     === 1

我们得到了A[B],它只是'false'[1] === 'a'

您可以在MDN上了解有关隐式类型转换运算符优先级的更多信息。

隐式类型转换是有经验的JavaScript程序员在比较值时更喜欢使用===而不是==的原因。


2

以下是详细的逐步过程:

( !! +[] + "" ) [ +!![] ]
//   ^^^ 

+[] 对数组字面量进行一元加操作,相当于 Number([]),结果为 0点击此处 查看为什么会得出 0 的解释。

( !! 0 + "" ) [ +!![] ]
//^^^^

!!0等同于!!Boolean(0)),它的值为false,因为0是假值。

( false + "" ) [ +!![] ]
//^^^^^^^^^^^
false+"" 是简单的字符串拼接,因此计算结果为 "false"
"false" [ +!![] ]
//         ^^^^ 

!![] 等同于 !!Boolean([]),由于对象的布尔转换始终为真,因此这将评估为true

"false" [ +true ]
//        ^^^^^

+true 等同于 Number(true),它的值为 1

"false" [ 1 ]

这里的关键点是Javascript在评估表达式时进行隐式类型转换或类型强制转换。要了解更多关于类型强制转换的信息,我建议阅读Axel Rauschmayer博士撰写的这个优秀资源。

类型强制转换

最终结果是a


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接