Почему ++ [[]] [+ []] + [+ []] возвращает строку «10»? - PullRequest
1576 голосов
/ 26 августа 2011

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

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

Почему?Что здесь происходит?

Ответы [ 9 ]

1992 голосов
/ 26 августа 2011

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

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

В JavaScript верно, что +[] === 0.+ преобразует что-то в число, и в этом случае оно уменьшается до +"" или 0 (см. Подробности спецификации ниже).

Следовательно, мы можем упростить это (++ имеет приоритетболее +):

++[[]][0]
+
[0]

Поскольку [[]][0] означает: получить первый элемент из [[]], верно, что:

  • [[]][0] возвращает внутреннеемассив ([]).Из-за ссылок неправильно говорить [[]][0] === [], но давайте вызовем внутренний массив A, чтобы избежать неправильных обозначений.
  • ++[[]][0] == A + 1, поскольку ++ означает «приращение на единицу».
  • ++[[]][0] === +(A + 1);другими словами, это всегда будет число (+1 не обязательно возвращает число, тогда как ++ всегда возвращает - спасибо Тим Даун за указание на это).

Опять же, мыможет упростить беспорядок в нечто более разборчивое.Давайте заменим [] на A:

+([] + 1)
+
[0]

В JavaScript это также верно: [] + 1 === "1", потому что [] == "" (присоединение к пустому массиву), поэтому:

  • +([] + 1) === +("" + 1) и
  • +("" + 1) === +("1") и
  • +("1") === 1

Давайте упростим это еще больше:

1
+
[0]

Кроме того, это верно в JavaScript: [0] == "0", потому что он объединяет массив с одним элементом.Присоединение объединит элементы, разделенные ,.С одним элементом вы можете сделать вывод, что эта логика приведет к самому первому элементу.

Итак, в конце мы получим (число + строка = строка):

1
+
"0"

=== "10" // Yay!

Спецификация для +[]:

Это довольно лабиринт, но чтобы сделать +[], сначала он преобразуется в строку, потому что + говорит:

11.4.6 Унарный + оператор

Унарный оператор + преобразует свой операнд в числовой тип.

Производство UnaryExpression: + UnaryExpression оценивается следующим образом:

  1. Пусть expr будет результатом вычисления UnaryExpression.

  2. Return ToNumber (GetValue (expr)).

ToNumber() говорит:

Объект

Примените следующие шаги:

  1. Пусть primValue будет ToPrimitive (входной аргумент, строка подсказки).

  2. Return ToString (primValue).

ToPrimitive() говорит:

Объект

Возвращает значение по умолчанию для объекта.Значение объекта по умолчанию извлекается путем вызова внутреннего метода [[DefaultValue]] объекта, передавая необязательную подсказку PreferredType.Поведение внутреннего метода [[DefaultValue]] определяется этой спецификацией для всех собственных объектов ECMAScript в 8.12.8.

[[DefaultValue]] говорит:

8.12.8 [[DefaultValue]] (подсказка)

Когда внутренний метод [[DefaultValue]] для O вызывается с hint String, предпринимаются следующие шаги:

  1. Пусть toString будет результатом вызова внутреннего метода [[Get]] объекта O с аргументом "toString".

  2. Если IsCallable (toString) равен true, тогда

a.Пусть str будет результатом вызова внутреннего метода [[Call]] для toString с O в качестве значения this и пустым списком аргументов.

b.Если str - примитивное значение, вернуть str.

.toString массива говорит:

15.4.4.2 Array.prototype.toString ()

Когда вызывается метод toString, предпринимаются следующие шаги:

  1. Пусть массив будет результатом вызова ToObject для значения this.

  2. Пусть func будет результатом вызова внутреннего метода [[Get]] массива с аргументом "join".

  3. Если IsCallable (func) равен false, тогда пусть func будетстандартный встроенный метод Object.prototype.toString (15.2.4.2).

  4. Возвращает результат вызова внутреннего метода [[Call]] для func, предоставляющего массив в качестве значения this и пустой список аргументов.

Итак, +[] сводится к +"", потому что [].join() === "".

Опять же, + определяется как:

11.4.6 Унарный + Оператор

Унарный оператор + преобразует свой операнд в числовой тип.

Производство UnaryExpression: + UnaryExpression оценивается следующим образом:

  1. Пусть expr будет результатом вычисления UnaryExpression.

  2. Return ToNumber (GetValue (expr)).

ToNumber определяется для "" как:

МЗ StringNumericLiteral ::: [пусто] равно 0.

То есть +"" === 0, а значит +[] === 0.

118 голосов
/ 26 августа 2011
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

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

1+[0].toString() = 10
59 голосов
/ 14 сентября 2011

Следующая статья взята из сообщения в блоге , отвечающего на этот вопрос, который я разместил, пока этот вопрос еще был закрыт.Ссылки на (HTML-копию) спецификации ECMAScript 3, по-прежнему являются базовыми для JavaScript в современных широко используемых веб-браузерах.

Во-первых, комментарий: такого рода выражения никогда не появятся ни в одном (нормальная) производственная среда и полезна только для того, чтобы понять, насколько хорошо читатель знает грязные грани JavaScript.Общий принцип, что операторы JavaScript неявно преобразуют между типами, полезен, как и некоторые из общих преобразований, но большая часть деталей в этом случае не такова.

Выражение ++[[]][+[]]+[+[]] может первоначально выглядеть довольно внушительным и неясным, но на самом деле сравнительно легко разбить на отдельные выражения.Ниже я просто добавил скобки для ясности;Я могу заверить вас, что они ничего не меняют, но если вы хотите проверить это, не стесняйтесь читать об операторе группировки .Таким образом, выражение может быть более четко записано как

( ++[[]][+[]] ) + ( [+[]] )

Разбивая это, мы можем упростить, наблюдая, что +[] оценивается как 0.Чтобы понять, почему это так, проверьте унарный + оператор и следуйте по слегка извилистому пути, который заканчивается ToPrimitive преобразованием пустого массива в пустую строку, которая затем окончательнопреобразуется в 0 на ToNumber .Теперь мы можем заменить 0 для каждого экземпляра +[]:

( ++[[]][0] ) + [0]

Уже проще.Что касается ++[[]][0], это комбинация оператора приращения префикса (++) и литерал массива , определяющий массив с одним элементом, который сам является пустым массивом (* 1033)*) и средство доступа к свойству ([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".Почему это происходит, оставлено читателю в качестве упражнения ...

23 голосов
/ 30 декабря 2011

Давайте сделаем это просто:

++[[]][+[]]+[+[]] = "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 голосов
/ 29 декабря 2011

Этот оценивает то же самое, но немного меньше

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

так что оценивается как

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

Так что теперь вы получили это, попробуйте это:

_=$=+[],++_+''+$
7 голосов
/ 26 августа 2011

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

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

4 голосов
/ 19 июня 2013

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

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10
    
0 голосов
/ 19 января 2019

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

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

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

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

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

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

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

Why does return the string “10”?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...