在阅读this article posted on dzone时,我发现了一段JavaScript posted on Twitter by Marcus Lagergren。
以下代码显然会打印字符串"fail"
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]];
这涉及隐式类型转换,我试图理解这条线的解释方式。
我已经隔离了每个角色
(![]+[])[+[]]
打印"f"
(![]+[])[+!+[]]
打印"a"
([![]]+[][[]])[+!+[]+[+[]]]
打印"i"
(![]+[])[!+[]+!+[]]
打印"l"
我还设法打破了除"i"
"f"
![]
一个空数组是一个Object,根据ECMAScript documentation, point 9.2转换为true
后,boolean
评估为false
,这是false+[]
+
根据Point 11.6.1,二进制"false"+""
运算符的两个参数都转换为String,因此得到"false"
,其中+[]
ToNumber
,则 ToPrimitive
一元加号运算符会导致Object
转换,然后进行[[DefaultValue]]
转换。通过调用对象的0
内部方法来确定此类转换的结果。如果是空数组,则默认为"false"[0]
。
(ECMAScript文档,部分:11.4.6,9.3,9.1)
0
我们正在访问索引"f"
处的字符,因此"a"
"false"
同样的故事,这里唯一的区别是方括号中的部分中的额外转换(其评估为指向字符串+
中的另一个字符的数字),由使用一元{{1}触发}和!
运营商。
+[]
评估为0
,如上所述。
!0
评估为Section 9.2和Section 11.4.9中定义的true
。首先,将0
转换为布尔值false
,然后运算符将该值反转。
+true
,一元加号会触发ToNumber
转化,该转化会为二元1
返回true
(Section 11.4.6和9.3)
"false"[1]
返回字符串中的第二个字符,即"a"
!+[]
评估为true
,如上所述
true+true
在基元上使用二进制+
会触发ToNumber
转换。如果为true,则结果为1
且1+1
等于2
"false"[2]
- 自我解释
"i"
让我感到难过的是"i"
字母。我可以看到第二部分(在方括号中)评估为字符串"10"
,第一部分(在括号中)返回"falseundefined"
但是我无法做出正确或反复的方式这种情况正在发生。有人可以一步一步地解释它吗?特别是方括号中出现的魔力? (数组和数组访问)
如果可能,我希望每个步骤都包含指向基础ECMAScript规则的链接。
我觉得最神秘的是这部分:[][[]]
答案 0 :(得分:36)
如果你稍微改写一下,你的神秘部分并不是那么神秘:
[]['']
[]
将被强制转换为字符串,因为它不是整数,因此您要查找名为[]
的{{1}}属性(空字符串)。您将获得''
,因为没有具有该名称的属性。
至于实际的字母,将表达式分为两个主要部分:
undefined
:
([![]]+[][[]])
是[![]]
。[false]
是[][[]]
。undefined
。 "falseundefined"
。一些空格和括号将使操作更清晰:[+!+[]+[+[]]]
:
[+(!(+[])) + [+[]]]
[+[]]
。[0]
将+[]
强制转换为整数,因此您获得[]
。0
将!+[]
强制转换为布尔值并取消它,因此您获得0
。true
将+!+[]
强制转换为整数,因此您获得 true
。1
。当使用字符串访问数组的属性并且字符串恰好是数组的元素时,字符串被强制转换为整数并且您将返回数组的实际元素:
["10"]
所以你的最终结果是:
> [1, 2, 3]["0"]
1
> [1, 2, 3]["1"]
2
阅读this answer,了解> "falseundefined"["10"]
"i"
部分的解释。
答案 1 :(得分:1)
([![]]+[][[]])[+!+[]+[+[]]]
有两部分:
([![]]+[][[]])
和你发现的另一个。
![]
返回false
。然后,我们使用[...]
获取.toString()
的{{1}}行为。
(+
与[]+[]
相同)
[].toString()+[].toString()
未定义,因为我们正在尝试访问未定义的[][[]]
索引[]
(或[].toString()
,''
)。
对于以前的回答,我很抱歉,我完全误读了您的评论。