Это допустимо и возвращает строку "10"
в JavaScript (больше примеров здесь):
console.log(++[[]][+[]]+[+[]])
Это допустимо и возвращает строку "10"
в JavaScript (больше примеров здесь):
console.log(++[[]][+[]]+[+[]])
Если мы разделим его, беспорядок равен:
++[[]][+[]]
+
[+[]]
В JavaScript верно, что +[] === 0
. +
преобразует что-то в число, и в этом случае он опустится до +""
или 0
(см. подробности ниже).
Таким образом, мы можем упростить его (++
имеет предсказуемость над +
):
++[[]][0]
+
[0]
Потому что [[]][0]
означает: получить первый элемент из [[]]
, это верно, что:
[[]][0]
возвращает внутренний массив ([]
). Из-за ссылок неверно говорить [[]][0] === []
, но позвольте внутреннему массиву A
избежать неправильной нотации.++[[]][0] == A + 1
, так как ++
означает "приращение на единицу".++[[]][0] === +(A + 1)
; другими словами, он всегда будет числом (+1
не обязательно возвращает число, тогда как ++
всегда делает - благодаря Tim Down для указания этого).Опять же, мы можем упростить беспорядок во что-то более разборчивое. Подставим []
назад для A
:
+([] + 1)
+
[0]
В JavaScript это также верно: [] + 1 === "1"
, потому что [] == ""
(объединение пустого массива), поэтому:
+([] + 1) === +("" + 1)
и+("" + 1) === +("1")
и+("1") === 1
Пусть это упростит еще больше:
1
+
[0]
Кроме того, это верно в JavaScript: [0] == "0"
, потому что он соединяет массив с одним элементом. Объединение объединяет элементы, разделенные символом ,
. С помощью одного элемента вы можете вывести, что эта логика приведет к самому первому элементу.
Итак, в итоге получим (number + string = string):
1
+
"0"
=== "10" // Yay!
Детали спецификации для +[]
:
Это довольно лабиринт, но для выполнения +[]
сначала он преобразуется в строку, потому что то, что +
говорит:
11.4.6 Унарный + оператор
Оператор unary + преобразует свой операнд в тип Number.
Произведение UnaryExpression: + UnaryExpression оценивается следующим образом:
Пусть expr является результатом вычисления UnaryExpression.
Возврат ToNumber (GetValue (expr)).
ToNumber()
говорит:
Объект
Примените следующие шаги:
Пусть primValue будет ToPrimitive (входной аргумент, подсказка String).
Возвращает ToString (primValue).
ToPrimitive()
говорит:
Объект
Возвращает значение по умолчанию для объекта. Значение по умолчанию для объекта извлекается, вызывая внутренний метод [[DefaultValue]] объекта, передавая необязательный подсказку PreferredType. Поведение внутреннего метода [[DefaultValue]] определяется этой спецификацией для всех собственных объектов ECMAScript в 8.12.8.
[[DefaultValue]]
говорит:
8.12.8 [[DefaultValue]] (подсказка)
Когда внутренний метод [[DefaultValue]] O вызывается с помощью строки подсказки, выполняются следующие шаги:
Пусть toString является результатом вызова внутреннего метода объекта [[Get]] объекта O с аргументом "toString".
Если IsCallable (toString) истинно, то
а. Пусть str является результатом вызова внутреннего метода [[Call]] toString, с O в качестве этого значения и пустым списком аргументов.
б. Если str является примитивным значением, верните str.
.toString
массива говорит:
15.4.4.2 Array.prototype.toString()
Когда вызывается метод toString, выполняются следующие шаги:
Пусть массив является результатом вызова ToObject для этого значения.
Пусть func является результатом вызова внутреннего метода [[Get]] массива с аргументом "join".
Если IsCallable (func) является ложным, то пусть func является стандартным встроенным методом Object.prototype.toString(15.2.4.2).
Возвращает результат вызова внутреннего метода func, предоставляющего метод [[Call]], как это значение, и список пустых аргументов.
Итак, +[]
сходит до +""
, потому что [].join() === ""
.
Опять же, +
определяется как:
11.4.6 Унарный + оператор
Оператор unary + преобразует свой операнд в тип Number.
Произведение UnaryExpression: + UnaryExpression оценивается следующим образом:
Пусть expr является результатом вычисления UnaryExpression.
Возврат ToNumber (GetValue (expr)).
ToNumber
определяется для ""
как:
MV строки StringNumericLiteral: [empty] равно 0.
So +"" === 0
, и, таким образом, +[] === 0
.
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
Тогда у нас есть конкатенация строк
1+[0].toString() = 10
Ниже приведена адаптация из сообщения в блоге, отвечая на этот вопрос, который я опубликовал, пока этот вопрос все еще закрыт. Ссылки представляют собой (копию HTML) спецификации ECMAScript 3, все еще базовую для JavaScript в современных широко используемых веб-браузерах.
Во-первых, комментарий: этот вид выражения никогда не появится в какой-либо (здоровой) производственной среде и будет использоваться только как упражнение только в том, насколько хорошо читатель знает грязные грани JavaScript. Общий принцип, что операторы JavaScript неявно конвертировать между типами полезен, а также некоторые из общих преобразований, но большая часть деталей в этом случае не является.
Выражение ++[[]][+[]]+[+[]]
может сначала выглядеть довольно внушительным и неясным, но на самом деле относительно легко разбивать на отдельные выражения. Ниже Ive просто добавил круглые скобки для ясности; Я могу заверить вас, что они ничего не меняют, но если вы хотите проверить, что тогда не стесняйтесь читать о оператора группировки. Таким образом, выражение может быть более четко записано как
( ++[[]][+[]] ) + ( [+[]] )
Разрушая это, мы можем упростить, заметив, что +[]
оценивается как 0
. Чтобы убедиться, почему это так, проверьте унарный + оператор и следуйте слегка извилистой дорожке, которая заканчивается ToPrimitive преобразование пустого массива в пустую строку, которая затем окончательно преобразуется в 0
с помощью ToNumber. Теперь мы можем заменить 0
для каждого экземпляра +[]
:
( ++[[]][0] ) + [0]
Проще уже. Что касается ++[[]][0]
, то это комбинация приращения приращения префиксов (++
), массив literal определение массива с одним элементом, который сам по себе является пустым массивом ([[]]
) и аксессуар свойств ([0]
), вызываемый в массиве, определяемом литералом массива.
Итак, мы можем упростить [[]][0]
до просто []
и имеем ++[]
, правильно? На самом деле это не так, потому что оценка ++[]
вызывает ошибку, которая может показаться запутанной. Тем не менее, небольшая мысль о природе ++
делает это ясным: он используется для увеличения переменной (например, ++i
) или свойства объекта (например, ++obj.count
). Он не только оценивает значение, но и сохраняет это значение где-то. В случае ++[]
ему некуда поставить новое значение (каким бы оно ни было), потому что нет ссылки на свойство или переменную объекта для обновления. В спецификациях это покрывается внутренней операцией PutValue, которая вызывается оператором приращения префикса.
Итак, что делает ++[[]][0]
? Ну, по аналогичной логике, как +[]
, внутренний массив преобразуется в 0
, и это значение увеличивается на 1
, чтобы дать нам окончательное значение 1
. Значение свойства 0
во внешнем массиве обновляется до 1
, и все выражение оценивается как 1
.
Это оставляет нас с
1 + [0]
..., который является простым использованием оператора добавления . Оба операнда первыми преобразуются в примитивы, и если примитивное значение является строкой, выполняется конкатенация строк, в противном случае выполняется цифровое добавление. [0]
преобразуется в "0"
, поэтому используется конкатенация строк, создавая "10"
.
В качестве окончательного в стороне, то, что может не сразу проявиться, заключается в том, что переопределение одного из методов toString()
или valueOf()
Array.prototype
изменит результат выражения, поскольку оба проверяются и используются, если они присутствуют при преобразовании объекта в примитивное значение. Например, следующие
Array.prototype.toString = function() {
return "foo";
};
++[[]][+[]]+[+[]]
... производит "NaNfoo"
. Почему это происходит, остается как упражнение для читателя...
Позволяет сделать это простым:
++[[]][+[]]+[+[]] = "10"
var a = [[]][+[]];
var b = [+[]];
// so a == [] and b == [0]
++a;
// then a == 1 and b is still that array [0]
// when you sum the var a and an array, it will sum b as a string just like that:
1 + "0" = "10"
Этот результат оценивается одинаково, но немного меньше
+!![]+''+(+[])
поэтому вычисляется
+(true) + '' + (0)
1 + '' + 0
"10"
Итак, теперь вы получили это, попробуйте следующее:
_=$=+[],++_+''+$
+ [] оценивается как 0 [...], а затем суммируя (+ операцию), он преобразует содержимое массива в его строковое представление, состоящее из элементов, соединенных с запятой.
Все, что угодно, например, принимать индекс массива (иметь более высокий приоритет, чем + операция), является порядковым и ничего интересного.
Возможно, кратчайшие пути оценки выражения в "10" без цифр:
+!+[] + [+[]]
// "10"
-~[] + [+[]]
// "10"
//========== Объяснение ==========\\
+!+[]
: +[]
Преобразует в 0. !0
преобразует в true
. +true
преобразуется в 1.
-~[]
= -(-1)
, который равен 1
[+[]]
: +[]
Преобразует в 0. [0]
- это массив с одним элементом 0.
Затем JS оценивает выражение 1 + [0]
, таким образом Number + Array
. Затем выполняется спецификация ECMA: оператор +
преобразует оба операнда в строку, вызывая функции toString()/valueOf()
из базового прототипа Object
. Он работает как аддитивная функция, если оба операнда выражения являются числами. Хитрость заключается в том, что массивы легко преобразуют свои элементы в конкатенированное строковое представление.
Некоторые примеры:
1 + {} // "1[object Object]"
1 + [] // "1"
1 + new Date() // "1Wed Jun 19 2013 12:13:25 GMT+0400 (Caucasus Standard Time)"
Хорошее исключение состоит в том, что два сложения Objects
приводят к NaN
:
[] + [] // ""
[1] + [2] // "12"
{} + {} // NaN
{a:1} + {b:2} // NaN
[1, {}] + [2, {}] // "1,[object Object]2,[object Object]"
+ '' или + [] оценивает 0.
++[[]][+[]]+[+[]] = 10
++[''][0] + [0] : First part is gives zeroth element of the array which is empty string
1+0
10
Шаг за шагом, +
превратить значение в число, и если вы добавите в пустой массив +[]
... так как он пустой и равен 0
, он будет
Итак, оттуда, теперь посмотрите на ваш код, это ++[[]][+[]]+[+[]]
...
И между ними есть плюс ++[[]][+[]]
+ [+[]]
Таким образом, эти [+[]]
вернут [0]
поскольку у них есть пустой массив, который преобразуется в 0
внутри другого массива...
Итак, представьте, что первое значение - это двумерный массив с одним массивом внутри... так что [[]][+[]]
будет равен [[]][0]
который будет возвращать []
...
И в конце ++
преобразовать его и увеличить до 1
...
Таким образом, вы можете себе представить, 1
+ "0"
будет "10"
...
Посмотрите на компилятор JSFuck и вот полный вывод.