Вы, наверное, видели всевозможный причудливый код JavaScript, написанный кем-то в Интернете с помощью нескольких разных символов, и это выглядит странно, но это работает! Например это:
(!(~+[])+{})[--[~+""][+[]]*[~+[]] + ~~!+[]]+({}+[])[[~!+[]]*~+[]]
Угадайте, каков результат? Можете зайти в консоль и попробовать сами.
Выглядит потрясающе, но что, черт возьми, здесь происходит?
На самом деле, вы можете написать почти любую программу на JavaScript, используя эти 6 символов:
[]()!+
Многие люди знают этот трюк, но не многие разработчики знают, как именно он работает. Сегодня давайте посмотрим на принцип выполнения, стоящий за этим. Наша цель — использовать эти несколько символов для записи строки“self”. Поприветствуйте язык Self с помощью этой строки, одного из источников вдохновения для JavaScript.
Фундаментальный
Мы можем опускать другие символы благодаря системе типов JavaScript и механизму преобразования типов данных.
Эти 6 символов выглядят следующим образом:[]можно использовать для создания массивов,!а также+Вы можете выполнить некоторые операции над массивом, а затем использовать()Сгруппируйте эти операции.
Сначала рассмотрим простой массив:
[]
добавить перед массивом!преобразует его в логическое значение. Массивы считаются истиннозначными, поэтому после отрицания становитсяfalse:
![] === false
Значения разных типов нельзя складывать вместе, если только они не преобразованы в аналогичные типы. JavaScript следует предопределенному правилу при выполнении преобразований:
в выражении2 + true, JavaScript будетtrueПреобразуйте в числа, чтобы получить выражение2+1.
в выражении2 + "2", JavaScript преобразует число в строку, чтобы получить2 + "2" === "22".
Эти правила преобразования не так уж плохи, но для других жанров грядут хорошие вещи.
Приведение массива JavaScript
Добавления массива преобразуются в строки и объединяются. Пустой массив преобразуется в пустую строку, поэтому добавление двух массивов приведет к пустой строке.
[] + [] === "" + "" === ""
То же самое верно и при добавлении массивов к другим типам значений:
![] + [] === "false" + "" === "false"
Удивлен или нет? мы получили целевую строку"self"Несколько персонажей включены!
Если мы сможем сгенерировать несколько чисел, мы сможем извлечь необходимые символы в правильном порядке:
"false"[3] === "s"
(![] + [])[3] === "s"
Итак, как генерировать числа?
генерировать числа
Как упоминалось ранее, вы можете преобразовывать массивы в логические значения. Тогда, если вы используете знак плюс+Как насчет того, чтобы преобразовать его в числа?
+[] === ???
JavaScript попытается вызвать массивvalueOfметод, но обнаруживается, что этого метода не существует, и тогда вместо него вызываетсяtoString()метод. Таким образом, приведенный выше код эквивалентен:
+[] === +""
Преобразование строки в число дает следующее:
+"42" === 42
+"esg" == NaN
+"" === 0
Пустая строка — этоfalseзначение, например null, undefined и число ноль, поэтому преобразование любого из них в число превратит их в ноль:
+null === 0
+undefined === 0
+false === 0
+NaN === 0
+"" === 0
Следовательно, преобразование массива в число требует его преобразования сначала в строку, а затем в 0:
+[] === +"" === 0
Создан первый номер! Нам еще нужны цифры, вперед:
!0 === !false
!false === true
!0 === true
Буду0Отрицательный, чтобы получить логическое значение, которое истинно. Значение true для преобразования логического значения в число, то есть1:
+true === 1
имеют1, естественно получить2, так называемыйДао рождает одно, одно рождает двоих, двое рождают троих, а трое рождают все вещи....
Используя описанный выше метод преобразования, мы можем легко получить нужные нам числа:
1 === +true == +(!0) ==== +(!(+[])) === +!+[]
1 === +!+[]
2 === +!+[] +!+[]
3 === +!+[] +!+[] +!+[]
4 === +!+[] +!+[] +!+[] +!+[]
Один шаг у двери, все готово
Подводя итог этим правилам:
- Аранжировки принадлежат истинным ценностям, поймите
false:![] // false - Массивы преобразуются в символы при добавлении:
[] + [] // "" - Преобразовать пустой массив в число
0, иди получайtrue, а затем преобразовать в числа, чтобы получить1:+(!(+[])) === 1
По этим правилам мы можем получить искомую строку. Это видно из схемы ниже:
![] + [] === "false"
+!+[] === 1
(![] + [])[3] + (![] + [])[4] + (![] + [])[2] + (![] + [])[0]
^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^
"false" "false" "false" "false"
^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^^^^^^^^^^^
s e l f
Окончательное выражение таково:
(![] + [])[+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]+!+[]+!+[]] +
(![] + [])[+!+[]+!+[]] +
(![] + [])[+[]]
После сортировки пробелов и новых строк это строка кода:
(![]+[])[+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]+!+[]]+(![]+[])[+[]]
Теперь вы должны понять принцип волшебного кода JavaScript? Используйте свое воображение, чтобы увидеть, что еще вы можете написать? Например, сегодня Рождество, приходите «Счастливого Рождества»?
В любом случае, с Рождеством!
Эта статья переведена и организована в соответствии с https://javascript.christmas/2019/17, и в нее добавлен некоторый собственный контент.
Больше галантереи передовых технологий можно найти в общедоступном аккаунте WeChat: станция перевода 1024.