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