Чому ++ [[]] [+ []] + [+ []] повертає рядок "10"?


1657

Це дійсно і повертає рядок "10"у JavaScript ( більше прикладів тут ):

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

Чому? Що тут відбувається?


446
Почніть з розуміння , що +[]кидає порожній масив , щоб 0... потім витрачати вечір ...;)
deceze


10
Погляньте на wtfjs.com - у ньому є дуже багато подібних речей із поясненнями.
ThiefMaster

3
@deceze, де ти дізнаєшся такого роду речі? Які книги? Я вчу JS з MDN, і вони цього не вчать
Siddharth Thevaril

6
@SiddharthThevaril Так само, як ви це робили: хтось десь десь розмістив про це повідомлення, і я випадково його прочитав.
деге

Відповіді:


2070

Якщо ми розділимо його, безлад дорівнює:

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

У 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 оцінюється наступним чином:

  1. Нехай expr є результатом оцінки UnaryExpression.

  2. Повернути ToNumber (GetValue (expr)).

ToNumber() каже:

Об'єкт

Застосуйте наступні дії:

  1. Нехай primValue буде ToPrimitive (вхідний аргумент, натяк String).

  2. Повернути ToString (primValue).

ToPrimitive() каже:

Об'єкт

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

[[DefaultValue]] каже:

8.12.8 [[DefaultValue]] (підказка)

Коли внутрішній метод O [[DefaultValue]] викликається з рядком підказки, виконуються наступні кроки:

  1. Нехай toString є результатом виклику внутрішнього методу [[Get]] об'єкта O з аргументом "toString".

  2. Якщо IsCallable (toString) відповідає дійсності,

а. Нехай str є результатом виклику внутрішнього методу [[Call]] toString, причому O це значення і порожній список аргументів.

б. Якщо str - це примітивне значення, поверніть str.

.toStringМасиву каже:

15.4.4.2 Array.prototype.toString ()

Коли викликається метод toString, виконуються наступні кроки:

  1. Нехай масив є результатом виклику ToObject на це значення.

  2. Нехай функція є результатом виклику внутрішнього методу масиву [[Get]] з аргументом "приєднатися".

  3. Якщо IsCallable (func) помилковий, то нехай func є стандартним вбудованим методом Object.prototype.toString (15.2.4.2).

  4. Поверніть результат виклику внутрішнього методу функціонування [[Call]] функціонального забезпечення масиву як цього значення та порожнього списку аргументів.

Так +[]зводиться до +"", тому що [].join() === "".

Знову ж таки, +визначається як:

11.4.6 Одинарний + Оператор

Оператор unary + перетворює свій операнд у тип Number.

Виробництво UnaryExpression: + UnaryExpression оцінюється наступним чином:

  1. Нехай expr є результатом оцінки UnaryExpression.

  2. Повернути ToNumber (GetValue (expr)).

ToNumberвизначається ""як:

MV для StringNumericLiteral ::: [порожній] дорівнює 0.

Отже +"" === 0, і таким чином +[] === 0.


8
@harper: Це сувора перевірка рівності, тобто повертається лише в тому trueвипадку, якщо значення і тип однакові. 0 == ""повертає true(те саме після перетворення типів), але 0 === ""є false(не однакових типів).
pimvdb

41
Частина цього неправильна. Вираз зводиться до 1 + [0], ні "1" + [0], тому що ++оператор префікса ( ) завжди повертає число. Дивіться bclary.com/2004/11/07/#a-11.4.4
Tim Down

6
@Tim Down: Ви абсолютно праві. Я намагаюся це виправити, але, намагаючись це зробити, я знайшов щось інше. Я не впевнений, як це можливо. ++[[]][0]справді повертається 1, але ++[]видає помилку. Це чудово, адже схоже ++[[]][0], що доводиться до кипіння ++[]. Ви, можливо, маєте ідею, чому ++[]викидає помилку, тоді як ++[[]][0]ні?
pimvdb

11
@pimvdb: Я впевнений, що проблема полягає у PutValueвиклику (у термінології ES3, 8.7.2) в операції з префіксом. PutValueвимагає посилання, тоді []як сам вираз не створює посилання. Вираз, що містить змінну посилання (скажімо, ми раніше визначали, var a = []то ++aпрацює) або доступ до властивостей об'єкта (наприклад, [[]][0]), створює Посилання. Простіше кажучи, оператор префікса не тільки створює значення, але і потрібно десь поставити це значення.
Tim Down

13
@pimvdb: Отже, після виконання var a = []; ++a, aце 1. Після виконання ++[[]][0]масив, створений [[]]виразом, містить просто число 1 в індексі 0. для цього ++потрібна посилання.
Тім Даун

123
++[[]][+[]] => 1 // [+[]] = [0], ++0 = 1
[+[]] => [0]

Тоді у нас є з'єднання рядків

1+[0].toString() = 10

7
Чи не було б зрозуміліше писати, ===а не =>?
Матін Ульхак

61

Далі адаптується публікація в блозі, що відповідає на це запитання, яке я опублікував, поки це питання все ще було закрито. Посилання на (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". Чому це трапляється, залишається читачем як вправа ...


24

Давайте спростимо:

++[[]][+[]]+[+[]] = "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
  • + !! [] - перетворює істинне в числове значення, яке оцінює в істинне, тому в цьому випадку 1
  • + '' - додає порожній рядок до виразу, викликаючи перетворення числа в рядок
  • + [] - оцінює до 0

так оцінюється до

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

Отже, тепер ви це зробили, спробуйте це:

_=$=+[],++_+''+$

Ну ні, це все ще оцінюється до "10". Однак це робиться по-іншому. Спробуйте оцінити це в інспекторі javascript, як хром чи щось.
Влад Шлосберг

_ = $ = + [], ++ _ + '' + $ -> _ = $ = 0, ++ _ + '' + $ -> _ = 0, $ = 0, ++ _ + '' + $ -> ++ 0 + '' + 0 -> 1 + '' + 0 -> '10' // Yei: v
LeagueOfJava

Цей оцінює те саме, але він навіть менший, ніж ваш:"10"
ADJenks

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. Унар плюс заданий рядок перетворюється на число
  2. Оператор приросту, який дає рядкові перетворення і збільшення на 1
  3. [] == ''. Порожня рядок
  4. + '' або + [] оцінює 0.

    ++[[]][+[]]+[+[]] = 10 
    ++[''][0] + [0] : First part is gives zeroth element of the array which is empty string 
    1+0 
    10

1
Відповідь заплутана / заплутана, IOW неправильна. []це НЕ еквівалентно "". Спочатку елемент витягується, потім перетворюється ++.
Вказане вуха

1

Крок за кроком цього, +поверніть значення до числа, і якщо ви додасте до порожнього масиву +[]... як він порожній і дорівнює 0, воно буде

Отже, звідти, тепер подивіться на свій код, це ++[[]][+[]]+[+[]]...

І є плюс між ними ++[[]][+[]]+[+[]]

Таким чином, вони [+[]]повернуться, [0]оскільки вони мають порожній масив, який перетворюється на 0інший масив ...

Отже, як уявіть, перше значення - це двовимірний масив з одним масивом всередині ... тому [[]][+[]]буде рівним тому, [[]][0]що повернеться []...

І наприкінці ++конвертуйте його та збільште до 1...

Тож ви можете собі уявити, 1+ "0"буде "10"...

Чому повертається рядок "10"?

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.