Це дійсно і повертає рядок "10"
у JavaScript ( більше прикладів тут ):
console.log(++[[]][+[]]+[+[]])
Чому? Що тут відбувається?
Це дійсно і повертає рядок "10"
у JavaScript ( більше прикладів тут ):
console.log(++[[]][+[]]+[+[]])
Чому? Що тут відбувається?
Відповіді:
Якщо ми розділимо його, безлад дорівнює:
++[[]][+[]]
+
[+[]]
У JavaScript це правда +[] === 0
. +
перетворює щось у число, і в цьому випадку воно зводиться до +""
або 0
(див. деталі специфікації нижче).
Тому ми можемо спростити це ( ++
має перевагу над +
):
++[[]][0]
+
[0]
Тому що [[]][0]
означає: отримати перший елемент від [[]]
, це правда, що:
[[]][0]
повертає внутрішній масив ( []
). Через посилання неправильно говорити [[]][0] === []
, але давайте зателефонуємо до внутрішнього масиву, A
щоб уникнути неправильних позначень.
++
перед його операндом означає «приріст на одиницю і повернення нарощеного результату». Так що ++[[]][0]
еквівалентно Number(A) + 1
(або +A + 1
).
Знову ж таки, ми можемо спростити безлад у щось більш розбірливе. Підставами []
назад на A
:
(+[] + 1)
+
[0]
Перш ніж +[]
можна примусити масив до числа 0
, його потрібно спочатку зв'язати в рядок, тобто ""
знову. Нарешті, 1
додається, що призводить до 1
.
(+[] + 1) === (+"" + 1)
(+"" + 1) === (0 + 1)
(0 + 1) === 1
Давайте ще більше спростимо це:
1
+
[0]
Також це справедливо в JavaScript:, [0] == "0"
тому що це об'єднання масиву з одним елементом. Приєднання об'єднає елементи, розділені на ,
. З одного елемента можна зробити висновок, що ця логіка спричинить за собою перший елемент.
У цьому випадку +
бачить два операнди: число та масив. Зараз він намагається примусити обох до одного типу. Спочатку масив примушується до рядка "0"
, далі число примушується до рядка ( "1"
). Кількість +
рядків ===
Рядок .
"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]] (підказка)
Коли внутрішній метод O [[DefaultValue]] викликається з рядком підказки, виконуються наступні кроки:
Нехай toString є результатом виклику внутрішнього методу [[Get]] об'єкта O з аргументом "toString".
Якщо IsCallable (toString) відповідає дійсності,
а. Нехай str є результатом виклику внутрішнього методу [[Call]] toString, причому O це значення і порожній список аргументів.
б. Якщо str - це примітивне значення, поверніть str.
.toString
Масиву каже:
15.4.4.2 Array.prototype.toString ()
Коли викликається метод toString, виконуються наступні кроки:
Нехай масив є результатом виклику ToObject на це значення.
Нехай функція є результатом виклику внутрішнього методу масиву [[Get]] з аргументом "приєднатися".
Якщо IsCallable (func) помилковий, то нехай func є стандартним вбудованим методом Object.prototype.toString (15.2.4.2).
Поверніть результат виклику внутрішнього методу функціонування [[Call]] функціонального забезпечення масиву як цього значення та порожнього списку аргументів.
Так +[]
зводиться до +""
, тому що [].join() === ""
.
Знову ж таки, +
визначається як:
11.4.6 Одинарний + Оператор
Оператор unary + перетворює свій операнд у тип Number.
Виробництво UnaryExpression: + UnaryExpression оцінюється наступним чином:
Нехай expr є результатом оцінки UnaryExpression.
Повернути ToNumber (GetValue (expr)).
ToNumber
визначається ""
як:
MV для StringNumericLiteral ::: [порожній] дорівнює 0.
Отже +"" === 0
, і таким чином +[] === 0
.
true
випадку, якщо значення і тип однакові. 0 == ""
повертає true
(те саме після перетворення типів), але 0 === ""
є false
(не однакових типів).
1 + [0]
, ні "1" + [0]
, тому що ++
оператор префікса ( ) завжди повертає число. Дивіться bclary.com/2004/11/07/#a-11.4.4
++[[]][0]
справді повертається 1
, але ++[]
видає помилку. Це чудово, адже схоже ++[[]][0]
, що доводиться до кипіння ++[]
. Ви, можливо, маєте ідею, чому ++[]
викидає помилку, тоді як ++[[]][0]
ні?
PutValue
виклику (у термінології ES3, 8.7.2) в операції з префіксом. PutValue
вимагає посилання, тоді []
як сам вираз не створює посилання. Вираз, що містить змінну посилання (скажімо, ми раніше визначали, var a = []
то ++a
працює) або доступ до властивостей об'єкта (наприклад, [[]][0]
), створює Посилання. Простіше кажучи, оператор префікса не тільки створює значення, але і потрібно десь поставити це значення.
var a = []; ++a
, a
це 1. Після виконання ++[[]][0]
масив, створений [[]]
виразом, містить просто число 1 в індексі 0. для цього ++
потрібна посилання.
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]
Тоді у нас є з'єднання рядків
1+[0].toString() = 10
===
а не =>
?
Далі адаптується публікація в блозі, що відповідає на це запитання, яке я опублікував, поки це питання все ще було закрито. Посилання на (HTML-копію) специфікації ECMAScript 3, як і раніше, є базовою лінією JavaScript у поширених сьогодні веб-браузерах.
По-перше, коментар: подібний вираз ніколи не з’явиться в будь-якому (розумному) виробничому середовищі і є корисним лише для того, наскільки читач знає брудні краї JavaScript. Загальний принцип, згідно з яким оператори JavaScript неявно перетворюють між типами, є корисним, як і деякі звичайні перетворення, але значна частина деталей у цьому випадку не є.
Вираз ++[[]][+[]]+[+[]]
може спочатку виглядати досить нав'язуючим і незрозумілим, але насправді досить легко розбити на окремі вирази. Нижче я просто додав дужки для чіткості; Можу запевнити, що вони нічого не змінюють, але якщо ви хочете перевірити це, тоді не соромтеся прочитати інформацію про оператора групування . Отже, вираз можна чіткіше записати як
( ++[[]][+[]] ) + ( [+[]] )
Розбиваючи це, ми можемо спростити, спостерігаючи, що +[]
оцінюється до 0
. Для того, щоб задовольнити себе , чому це так, перевірте унарний оператор + і слідувати злегка звивистому сліду , який закінчується з ToPrimitive перетворення порожнього масиву в порожній рядок, яка потім , нарешті , перетворюється в 0
по ToNumber . Тепер ми можемо замінити 0
кожен примірник +[]
:
( ++[[]][0] ) + [0]
Простіше вже. Що стосується ++[[]][0]
, це комбінація оператора збільшення префікса ( ++
), літералу масиву, що визначає масив з єдиним елементом, який сам є порожнім масивом ( [[]]
), і властивість accessor ( [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"
Отже, тепер ви це зробили, спробуйте це:
_=$=+[],++_+''+$
"10"
Мабуть, найкоротші можливі способи оцінити вираз на "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"
...
+[]
кидає порожній масив , щоб0
... потім витрачати вечір ...;)