Почему ++ [[]] [+ []] + [+ []] возвращает строку «10»?

1511

Это допустимо и возвращает строку "10" в JavaScript (больше примеров здесь):

console.log(++[[]][+[]]+[+[]])

Зачем? Что здесь происходит?

  • 386
    Начните с понимания того, что +[] приводит пустой массив к 0 ... затем теряете день ...;)
  • 14
    Связанный stackoverflow.com/questions/4170978/explain-why-this-works .
Показать ещё 13 комментариев
Теги:
syntax

10 ответов

1843
Лучший ответ

Если мы разделим его, беспорядок равен:

++[[]][+[]]
+
[+[]]

В 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.

  • 0
    как вы переходите с ++[[]][0] на [] + 1 ?
  • 0
    @Lie Райан: [[]][0] это то, что вложено [] . ++ увеличивается на единицу, поэтому он возвращает [] + 1 .
Показать ещё 12 комментариев
114
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Тогда у нас есть конкатенация строк

1+[0].toString() = 10
  • 6
    Надеюсь, что все ответы будут прямо к сути, как это.
  • 0
    Разве не было бы яснее написать === а не => ?
60

Ниже приведена адаптация из сообщения в блоге, отвечая на этот вопрос, который я опубликовал, пока этот вопрос все еще закрыт. Ссылки представляют собой (копию 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". Почему это происходит, остается как упражнение для читателя...

20

Позволяет сделать это простым:

++[[]][+[]]+[+[]] = "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"
13

Этот результат оценивается одинаково, но немного меньше

+!![]+''+(+[])
  • [] - преобразуется массив, который преобразуется в 0 при добавлении или вычитании из него, поэтому, следовательно, + [] = 0
  • ! [] - оценивается как false, поэтому отсюда. [] оценивается как true
  • +!! [] - преобразует значение true в числовое значение, которое вычисляется как true, поэтому в этом случае 1
  • + '' - добавляет пустую строку в выражение, заставляя число преобразовываться в строку
  • + [] - оценивается как 0

поэтому вычисляется

+(true) + '' + (0)
1 + '' + 0
"10"

Итак, теперь вы получили это, попробуйте следующее:

_=$=+[],++_+''+$
  • 0
    какой ответ? '010'?
  • 0
    Ну нет, это все равно оценивается в «10». Однако это делает это по-другому. Попробуйте оценить это в инспекторе javascript, таком как chrome или что-то в этом роде.
Показать ещё 1 комментарий
7

+ [] оценивается как 0 [...], а затем суммируя (+ операцию), он преобразует содержимое массива в его строковое представление, состоящее из элементов, соединенных с запятой.

Все, что угодно, например, принимать индекс массива (иметь более высокий приоритет, чем + операция), является порядковым и ничего интересного.

4

Возможно, кратчайшие пути оценки выражения в "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]"
1
  • Унарная плюс заданная строка преобразуется в число
  • Оператор приращения данной строки преобразует и увеличивает на 1
  • [] == ''. Пустая строка
  • + '' или + [] оценивает 0.

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    
  • 1
    Ответ запутан / запутан, IOW неправильный. [] не эквивалентно "" . Сначала элемент извлекается, затем конвертируется с помощью ++ .
0

Посмотрите на компилятор JSFuck и вот полный вывод.

0

Шаг за шагом, + превратить значение в число, и если вы добавите в пустой массив +[]... так как он пустой и равен 0, он будет

Итак, оттуда, теперь посмотрите на ваш код, это ++[[]][+[]]+[+[]]...

И между ними есть плюс ++[[]][+[]] + [+[]]

Таким образом, эти [+[]] вернут [0] поскольку у них есть пустой массив, который преобразуется в 0 внутри другого массива...

Итак, представьте, что первое значение - это двумерный массив с одним массивом внутри... так что [[]][+[]] будет равен [[]][0] который будет возвращать []...

И в конце ++ преобразовать его и увеличить до 1...

Таким образом, вы можете себе представить, 1 + "0" будет "10"...

Изображение 2135

Ещё вопросы

Сообщество Overcoder
Наверх
Меню