Почему и как ([! []] + [] [[]]) [+! + [] + [+ []]] Оценивает букву "i"?
При чтении этой статьи, размещенной на dzone, я нашел фрагмент JavaScript, первоначально размещенный в Twitter Маркусом Лагергеном.
Следующий код, по-видимому, печатает строку "fail"
(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]];
Это подразумевает неявное кастинг типов, и я пытаюсь понять, как именно эта строка интерпретируется.
Я выделил каждый символ
-
(![]+[])[+[]]
prints "f"
-
(![]+[])[+!+[]]
печатает "a"
-
([![]]+[][[]])[+!+[]+[+[]]]
печатает "i"
-
(![]+[])[!+[]+!+[]]
prints "l"
Мне также удалось сломать выражения, возвращающие каждую букву, кроме "i"
письмо "f"
![]
пустой массив - это объект, который согласно документации ECMAScript, пункт 9.2, оценивается до true
при преобразовании в boolean
, так что это false
false+[]
в соответствии с пунктом 11.6.1 оба аргумента двоичного оператора +
преобразуются в String, поэтому мы получаем "false"+""
, который оценивает "false"
+[]
Унарный оператор plus вызывает преобразование ToNumber
, за которым следует преобразование ToPrimitive
, если аргумент равен Object
. Результат такого преобразования определяется вызовом внутреннего метода [[DefaultValue]]
объекта. В случае пустого массива он по умолчанию равен 0
.
(Документация ECMAScript, разделы: 11.4.6, 9.3, 9.1)
"false"[0]
мы обращаемся к символу с индексом 0
, поэтому "f"
письмо "a"
В той же истории единственная разница здесь - это дополнительные преобразования в части в квадратных скобках (которая вычисляет число, указывающее на другой символ в строке "false"
), вызванный использованием унарных +
и !
операторов.
+[]
оценивается как 0
, как описано выше.
!0
оценивается как true
, как определено в разделе Раздел 9.2 и Раздел 11.4.9. Во-первых, 0
преобразуется в boolean false
, а затем оператор инвертирует значение.
+true
снова, унарный плюс запускает преобразование ToNumber
, которое возвращает 1
для двоичного true
(раздел 11.4.6 и 9.3)
"false"[1]
возвращает второй символ в строке, который равен "a"
буква "l"
!+[]
оценивается как true
, как описано выше
true+true
, используя двоичный +
для примитивов, запускает преобразование ToNumber
. В случае истины его результат равен 1
и 1+1
равен 2
"false"[2]
- самоочевидный
письмо "i"
Что оставляет меня в тупике - это письмо "i"
. Я вижу, что вторая часть (в квадратных скобках) оценивает строку "10"
и что первая часть (в круглых скобках) возвращает "falseundefined"
, но Я не могу делать головы или хвосты того, как это происходит. Может кто-нибудь объяснить это шаг за шагом? Особенно волшебство, которое случается с квадратными скобками? (массивы и доступ к массиву)
Если возможно, я хотел бы, чтобы каждый шаг содержал ссылку на основные правила ECMAScript.
То, что я нахожу самым загадочным, - это эта часть: [][[]]
Ответы
Ответ 1
Ваша загадочная часть не такая загадочная, если вы ее немного переписываете:
[]['']
[]
будет принудительно введен в строку, потому что это не целое число, поэтому вы ищете свойство []
с именем ''
(пустая строка). Вы просто получите undefined
, так как нет свойства с этим именем.
Что касается фактической буквы, сложите выражение вверх на два основных компонента:
- Строка
([![]]+[][[]])
:
-
[![]]
[false]
.
-
[][[]]
- undefined
.
- Добавьте их вместе, и вы получите
"falseundefined"
.
- И индекс:
[+!+[]+[+[]]]
. Некоторые пробелы и круглые скобки сделают операции более ясными: [+(!(+[])) + [+[]]]
:
-
[+[]]
[0]
.
-
+[]
принуждает []
к целому числу, поэтому вы получаете 0
.
-
!+[]
принуждает 0
к булевому и отрицает его, поэтому вы получаете true
.
-
+!+[]
заставляет true
целое число, поэтому вы получаете 1
.
- Добавьте их вместе, и вы получите
["10"]
.
При использовании строки для доступа к свойствам массива, и строка оказывается элементом массива, строка принудительно вводится в целое число и вы возвращаете фактический элемент массива:
> [1, 2, 3]["0"]
1
> [1, 2, 3]["1"]
2
Итак, ваш конечный результат:
> "falseundefined"["10"]
"i"
Прочитайте этот ответ для объяснения части [false] + undefined
.
Ответ 2
([![]]+[][[]])[+!+[]+[+[]]]
имеет две части:
([![]]+[][[]])
, а другой, который вы нашли.
![]
возвращает false
. Затем мы используем [...]
для получения .toString()
поведения +
.
([]+[]
совпадает с [].toString()+[].toString()
)
[][[]]
- undefined, потому что мы пытаемся получить доступ к индексу []
(или [].toString()
, который является ''
) []
, который равен undefined.
Извините за предыдущий ответ, я неправильно прочитал ваш комментарий.