Чому! {} [True] оцінює справжнє в JavaScript?


131

{}[true]є [true]і ![true]має бути false.

То чому ж !{}[true]оцінювати true?


30
var o = {}; o[true] === undefined.
azz

2
Пояснення тут, ймовірно, буде дуже схожим на дивацтва, обговорені в цьому попередньому запитанні
IMSoP

45
"Тому що Javascript нерозумно" - це, мабуть, не відповідь, яку ви шукаєте.
georg

2
Як згадувалося, якщо ви отримуєте {}[true] === [true]консоль, це тому, що вона трактується {}як порожній код коду, а не як об'єкт.
azz

3
якщо це може допомогти, спробуйте порівняти {}і ({})в консолі (або {}[true]і ({})[true]). Крім того, як ніхто не згадував про це, об'єкт [true] оцінюється як об’єкт ["true"].
BiAiB

Відповіді:


172

Я вважаю, це тому, що plain {}[true]розбирається як порожній блок операторів (а не об'єктний буквал), за яким слідує масив true, що містить true.

З іншого боку, застосування !оператора змушує аналізатор інтерпретувати {}як об'єкт буквально, тому наступний {}[true]стає членом доступу, який повертається undefined, і !{}[true]справді є true(як !undefinedє true).


25
Той факт, що! Undefined є правдою, з іншого боку, все ще невиправдано.
evilcandybag

87
@evilcandybag: Це абсолютно не так. undefinedє помилковим (те, на що ми часто покладаємось - if (obj.maybeExists) ...), тому воно має ідеальний логічний сенс, що !undefinedє правдою.
josh3736

8
@Josh, я думаю, що evilcandybag вважає за краще поведінку, подібну до nullдеяких мов, при !undefinedрівності undefined. Однак у Javascript це не так.
Фредерік Хаміді

6
@evilcandybag: це має лише логічний сенс сказати, що щось таке, що є not undefined( !undefined), повинно бути визначене. Якщо щось визначено, то це зазвичай трактується як true.
OozeMeister

7
@Cruncher Якщо a не визначено, а b не визначено, як ми можемо знати, що a! = B? Особливо, коли єдина відома характеристика двох змінних абсолютно однакова.
LJ2

44

Тому {}[true]що не повертається true, але undefinedі undefinedоцінюється як false:

http://jsfiddle.net/67GEu/

'use strict';
var b = {}[true];
alert(b); // undefined
b = !{}[true];
alert(b); // true

21
Якщо ви оцінюєте {}[true]в консолі, ви отримуєте [true], оскільки {}інтерпретується як порожній код коду, а не об'єкт. Вся справа в контексті та неоднозначності {}.
IMSoP

1
@IMSoP, але чому {key:"value"}[1,2,3];також оцінювати [1,2,3]?
t.niese

3
@ t.niese, тому що він розбирається як блок заяви, що містить label ( key:) та рядковий літерал ( "value"), за яким слідує масив. Парсер все ще не бачить об'єкта буквально.
Фредерік Хаміді

1
@ FrédéricHamidi ах, так, це все. Я репресував етикетки ^^
t.niese

1
@dooxe Прочитайте інші відповіді; мова йде про контекст, в якому він інтерпретується. Якщо ви укладаєте його в alert()або console.log(), або привласнити його змінної, ви змінюєте контекст, тому він не поводиться так само, як і набрана на свій власний в консолі.
IMSoP

27

Тому що

{}[true]

оцінює до undefinedта !undefinedєtrue .

Від @schlingel:

trueвикористовується як ключ і {}як хеш-карта. Не існує властивості з ключем, trueтому він повертається undefined. Не undefinedєtrue , як очікувалося.

Сеанс консолі ( Node.js [0.10.17] ):

> {}[true]
undefined
> !{}[true]
true
> [true]
[ true ]
> ![true]
false
>

Однак у консолі Google Chrome :

> !{}[true]
true

Отже, ніяких невідповідностей. Ви, ймовірно, використовуєте стару версію JavaScript VM. Для тих, хто потребує додаткових доказів:

Введіть тут опис зображення

ОНОВЛЕННЯ

За допомогою Firefox він також оцінює true:

Введіть тут опис зображення


Ні, якщо ви це робите eval('{}[true]')чи вводите для консолі. Тоді наприклад als {}"test"є testабо навіть {key:"value"}"test"є test.
t.niese

Цікаво, в якому js двигуні ви це тестуєте?
t.niese

@ t.niese Я просто набрав його на консолі свого вузла, і ось що я отримав.
Ігри Brainiac

Просто для цікавості. Чи повертається {}[true];(за ;) [true]до вас, бо тут це відбувається?
t.niese

2
Підстава для парубочих хлопців? На це майже однакова відповідь з 8 голосами, і я отримую голосування? Що я зробив не так?
Ігри Brainiac

23

Причина плутанини зводиться до нерозуміння вашого першого твердження:

{}[true] є [true]

Те, що ви бачите, коли ви запускаєте це, є результатом неоднозначності. У Javascript встановлено певний набір правил, як керувати двозначностями, як це, і в цьому випадку він розбиває те, що ви бачите як твердження сигматики, на дві окремі заяви.

Тож Javascript розглядає вищезазначений код як два окремих твердження: По-перше, є a {}, а потім є зовсім окремим [true]. Друге твердження - це те, що дає тобі результат [true]. Перше твердження {}фактично повністю ігнорується.

Ви можете довести це, спробувавши наступне:

({}[true])

тобто загортання всієї справи в дужки, щоб змусити перекладача прочитати це як єдине твердження.

Тепер ви побачите, що фактична цінність вашої заяви undefined. (це також допоможе нам пізніше зрозуміти наступну частину)

Тепер ми знаємо, що початкова частина вашого запитання - це червона оселедець, тому перейдемо до заключної частини запитання:

Так чому ж {{[істина] оцінює справжнє?

Тут ми маємо те саме твердження, але з !доданим до нього фронтом.

У цьому випадку правила Javascript повідомляють це, щоб він оцінював всю річ як єдине твердження.

Зверніться до того, що сталося, коли ми загорнули попередні твердження в дужки; ми отрималиundefined . Цього разу ми ефективно робимо те саме, але ставимо !перед цим. Отже ваш код можна спростити як !undefined, що є true.

Сподіваємось, це трохи пояснює це.

Це складний звір, але урок, який слід тут вивчити, - це використовувати дужки навколо своїх тверджень, оцінюючи їх у консолі, щоб уникнути таких помилкових результатів.


2
Я не думаю , що {}[true]це неприпустимий точно, просто неоднозначним . Його можна інтерпретувати як "порожній блок коду з наступним літералом масиву", або "об'єктний літерал без властивостей, доступ до якого властивість доступний". Я не знаю, чи перший технічно є випадком ASI (багато мов так чи інакше не ставлять би крапку з двократкою), але саме в контексті цього є інтерпретація, залежна від контексту.
IMSoP

@IMSoP - Я вже відредагував відповідь, перш ніж ви опублікували коментар. :)
Спудлі

1
На початку відповіді все ще сказано, що "{} [правда] насправді взагалі не дійсна".
IMSoP

Також ОП не сказала " {}[true]є true", вони сказали " {}[true]є [true]", що є одним з двох дійсних тлумачень неоднозначного твердження.
IMSoP

14

{}[true]є undefined. Щоб знайти це, напишіть це:

a = {};
a[true] === undefined // true

або просто:

({})[true] === undefined // true

Ми знаємо, що !undefinedє true.


З відповіді @Benjamin Gruenbaum :

Інструменти для розробників Chrome :

  try {
      if (injectCommandLineAPI && inspectedWindow.console) {
          inspectedWindow.console._commandLineAPI = new CommandLineAPI(this._commandLineAPIImpl, isEvalOnCallFrame ? object : null);
          expression = "with ((window && window.console && window.console._commandLineAPI) || {}) {\n" + expression + "\n}";
      }
      var result = evalFunction.call(object, expression);
      if (objectGroup === "console")
          this._lastResult = result;
      return result;
  } 
  finally {
      if (injectCommandLineAPI && inspectedWindow.console)
          delete inspectedWindow.console._commandLineAPI;
  }

Таким чином, він виконує callвираз на об'єкті з виразом. Вираз:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

Отже, як бачите, вираз оцінюється безпосередньо, без обертових дужок.

Більше інформації можна знайти в цьому запитанні .


10

Відповіді тут хороші, ось розбивка псевдокоду:

  • {}['whatever'] = порожній блок, NewArray ("що завгодно") = NewArray ("що завгодно")
  • {}[true] = порожній блок, NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (convertToBool (NewObject.wever)) = LogicalNOT (convertToBool (не визначено)) = LogicalNOT (помилково) = вірно
  • ({}['whatever']) = Групування (NewObject.wever) = Групування (невизначено) = невизначено

8

Це трапляється тому, що {}у вашому розумінні є не буквальне представлення Object, а порожня область (або порожній код коду):

{ var a = 1 }[true] // [true] (do the same thing)

Він просто оцінює код у межах дії, а потім показує вам масив.

І від вашого

!{}[true]

Просто перетворює в int цю область і повертає той же масив true. У цьому коді немає перевірок bool.

І якщо ви спробуєте перевірити результат, {}[true]ви отримаєте false:

{}[true] -> [true] -> ![true] -> false

Так як сфери немає більше.

Тож !у вашому питанні зробіть те саме, що:

!function() {
   //...
}

Це легше помітити, якщо це зробити var x = {}; x[true].
Кріс Хейс

1
Я не впевнений, що ви маєте на увазі під "перетворює в цілий діапазон"; Я думаю , що з провідним !це буде витлумачено як порожній об'єкт, а не сфера, і це невідповідність.
IMSoP

6
  • {} є об'єктом без властивостей.
  • Оскільки []відразу слідує об’єкт, це означає "Отримати доступ до властивості цього імені", а не "Створити масив"
  • trueє булевим, але використовується як ім'я властивості, тому воно передається в рядок ( "true")
  • Об'єкт не має властивості true(так як він не має властивостей) , так {}['true']цеundefined
  • !undefinedкидає undefinedна булевий ( false)
  • Не оператор перетворюється falseна true.

2
У випадку {}[true](якщо немає іншого контексту), {}це не об'єкт без властивостей, це порожній код коду.
IMSoP


4

Давайте пограємо ще трохи!

Спочатку давайте повеселитися !:

//----------#01#-----------
{}[true]; //[true]

//----------#02#-----------
var a = {}[true]; 
      console.log(a); //undefined

//----------#03#-----------
{ b: 12345 }[true]; //[true]

//----------#04#-----------
{ b: 12345 }["b"]; //evaluates to ["b"] ?!?

//----------#05#-----------
{ b: 12345 }.b; // "Unexpected token ."

//----------#06#-----------
({ b: 12345 }).b; //12345

//----------#07#-----------
var c = { b: 12345 }.b; 
      console.log(c); //12345

//----------#08#-----------
var c = { b: 12345 }["b"];
      console.log(c); //12345

//----------#09#-----------
{ true: 54321 }[true]; // "SyntaxError: Unexpected token : "

//----------#10#-----------
var d = { true: 54321 }[true]; //No error here ¬¬
      console.log(d); //54321

//----------#11#-----------
!{}[true]; // true

Гаразд, спробуємо зрозуміти ці шалені поведінки по черзі:

1) Тут {}розбирається як порожній блок коду. Без призначення, заперечення, групування (з дужками) або будь-якого синтаксису, який вказує на аналізатор, що це{} об'єкт буквально, припущення за замовчуванням - це думати, що це просто марний порожній блок.

Це є доказом такої поведінки:

{ alert(123) }[true]

Код, наведений вище, відображатиме сповіщення як правило, і оцінюватиметься як [true]такий самий спосіб{}[true] є.

Блокові виписки без крапки з комою

Оператор блокового типу після нього не потребує крапки з комою.

Наприклад:

for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")

Показано обидва сповіщення.

Отже, ми можемо бачити, що порожній блок заяви без крапки з комою є дійсним і просто нічого не робить. Таким чином, коли ви входите {}[true]в консоль інструментів розробника (або Firebug), оцінене значення буде значенням останнього оператора виразів . У цьому випадку останній вираз вираз є [true].

2) У контексті присвоєння аналізатор переконається, що {}це об'єкт буквально. Коли ви робите var a = {}[true], ви видаляєте будь-яку неоднозначність і підказуєте аналізатор, який {}не є оператором блоку.
Отже, тут ви намагаєтеся отримати значення з ключем "true"від порожнього об'єкта. Очевидно, що немає пари ключ-значення з цим ім'ям ключа. Таким чином, змінна не визначена.

Зарезервовані слова як клавіші об'єкта

ECMAScript 5 дозволяє клавішам об'єктів бути зарезервованими словами. Отже, наступні ключі є законними:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3) Те саме пояснення прикладу 1 . Але ... Якщо { b: 12345 }частина трактується як блок-оператор, то який тип b: 12345висловлювання ??

... (?????)

Це твердження мітки , ви вже бачили його раніше ... Він використовується в циклі та в switch. Ось кілька цікавих посилань щодо тверджень про мітки: 1 , (2) [ Найкращий спосіб перервати з вкладених циклів у Javascript? , (3) [ Як розбити вкладені петлі в JavaScript? .

ПРИМІТКА. Просто спробуйте оцінити це:

{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :

Оператори міток не можуть бути відокремлені оператором коми , вам потрібно буде відокремити їх крапкою з комою. Отже, це дійсно:{a: 1; b: 2}

4) Див пояснення до прикладів 1 і 3 ...

5) Ще один раз, коли ми { b: 12345 }трактуємось як блок коду, і ви намагаєтеся отримати доступ до властивості блоку коду, використовуючи позначення крапок , і, очевидно, це заборонено, і аналізатор кидає "Unexpected token :"виняток.

6) Код майже ідентичний наведеному вище прикладу, але, оточуючи { b: 12345 }оператор з оператором групування виразів , аналізатор буде знати, що це об'єкт. Таким чином, ви зможете "b"нормально отримати доступ до ресурсу.

7) Запам’ятайте приклад 2 , у нас тут є призначення, аналізатор знає, що { b: 12345 }це об’єкт.

8) Ідентично вищенаведеному прикладу, але замість позначення крапки тут ми використовуємо позначення дужок .

9) Я вже казав, що цей "identifier: value"синтаксис всередині оператора блоку є міткою. Але ви також повинні знати, що ім'я мітки не може бути зарезервованим ключовим словом (навпаки імен властивостей об'єкта). Коли ми намагалися визначити мітку під назвою "true", ми отримали SyntaxError.

10) Знову ми маємо справу з об’єктом. Тут немає проблем із використанням зарезервованих слів. =)

11) Нарешті, ми маємо це:!{}[true]

Давайте розділимо тут речі:

а) Роблячи заперечення, ми інформуючи аналізатор , що {}є об'єктом .

b) Як показано в прикладі 2 , {}об'єкт не має властивості, яку називають true, тому цей вираз буде оцінено до undefined.

в) Кінцевим результатом є заперечення undefinedвартості. Javascript виконує перетворення типу імпліцитності , а undefinedзначення - хибне .

г) Отже, заперечення - falseце ... true!

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