为什么以及如何([![]] + [] [[]])[+!+ [] + [+ []]]评估为字母“i”?

时间:2013-06-09 22:26:26

标签: javascript types obfuscation ecmascript-5

在阅读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.69.39.1

0我们正在访问索引"f"处的字符,因此"a"

字母"false"

同样的故事,这里唯一的区别是方括号中的部分中的额外转换(其评估为指向字符串+中的另一个字符的数字),由使用一元{{1}触发}和!运营商。

+[]评估为0,如上所述。

!0评估为Section 9.2Section 11.4.9中定义的true。首先,将0转换为布尔值false,然后运算符将该值反转。

再次

+true,一元加号会触发ToNumber转化,该转化会为二元1返回trueSection 11.4.69.3

"false"[1]返回字符串中的第二个字符,即"a"

字母“l”

!+[]评估为true,如上所述

true+true在基元上使用二进制+会触发ToNumber转换。如果为true,则结果为11+1等于2

"false"[2] - 自我解释

字母"i"

让我感到难过的是"i"字母。我可以看到第二部分(在方括号中)评估为字符串"10",第一部分(在括号中)返回"falseundefined"但是我无法做出正确或反复的方式这种情况正在发生。有人可以一步一步地解释它吗?特别是方括号中出现的魔力? (数组和数组访问)

如果可能,我希望每个步骤都包含指向基础ECMAScript规则的链接。

我觉得最神秘的是这部分:[][[]]

2 个答案:

答案 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()'')。

对于以前的回答,我很抱歉,我完全误读了您的评论。