Почему! {} [True] оценивается как true в JavaScript?

131

{}[true]есть [true]и ![true]должно быть false.

Так зачем !{}[true]оценивать true?

user2430508
источник
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
@ Джош, я думаю, что 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
dooxe
источник
21
Если вы выполняете оценку {}[true]в консоли, вы получаете [true], потому что {}интерпретируется как пустой блок кода, а не как объект. Все дело в контексте и неоднозначности {}.
IMSoP
1
@IMSoP, но почему еще {key:"value"}[1,2,3];оценивает [1,2,3]?
t.niese
3
@ t.niese, потому что он анализируется как блок операторов, содержащий метку ( key:) и строковый литерал ( "value"), за которыми следует массив. Парсер по-прежнему не видит литерал объекта.
Frédéric Hamidi
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. Для тех, кому нужны дополнительные доказательства:

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

ОБНОВИТЬ

В Firefox он также оценивает true:

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

Игры Brainiac
источник
Нет, если вы это сделаете eval('{}[true]')или напечатаете в консоли. Тогда, например, als {}"test"есть testили даже {key:"value"}"test"есть test.
t.niese
Интересно, на каком js-движке вы это тестируете?
t.niese
@ t.niese Я просто набрал его в консоли узла, и вот что у меня получилось.
Games Brainiac
Просто из любопытства. Возвращается ли {}[true];;) [true]для вас, потому что оно здесь?
t.niese
2
Причина отрицательного голоса, ребята? На этот есть почти идентичный ответ с 8 голосами, и я получаю отрицательный? Что я сделал не так?
Games Brainiac
23

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

{}[true] является [true]

То, что вы видите при запуске, является результатом двусмысленности. Javascript имеет определенный набор правил относительно того, как обрабатывать подобные неоднозначности, и в этом случае он разбивает то, что вы видите как оператор signle, на два отдельных оператора.

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

Вы можете доказать это, попробовав следующее:

({}[true])

т.е. заключить все в квадратные скобки, чтобы интерпретатор мог прочитать это как отдельную инструкцию.

Теперь вы увидите, что фактическая ценность вашего утверждения равна undefined . (это также поможет нам позже понять следующую часть)

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

Так почему же! {} [True] оценивается как true?

Здесь у нас есть то же утверждение, но с ! добавленным перед ним.

В этом случае правила Javascript говорят ему оценивать все как одно выражение.

Вернитесь к тому, что произошло, когда мы заключили предыдущее утверждение в скобки; мы получили undefined. На этот раз мы фактически делаем то же самое, но ставим !перед ним символ. Таким образом, ваш код можно упростить как !undefined:true .

Надеюсь, это немного это объясняет.

Это сложный зверь, но урок, который следует усвоить здесь, заключается в том, чтобы использовать скобки вокруг ваших операторов при их оценке в консоли, чтобы избежать подобных ложных результатов.

Spudley
источник
2
Я не считаю {}[true]это недействительным , просто двусмысленным . Его можно интерпретировать либо как «пустой блок кода, за которым следует литерал массива», либо как «литерал объекта без свойств, к свойству которого осуществляется доступ». Я не знаю, является ли первый технически случаем ASI (многие языки все равно не ставят точку с запятой), но суть проблемы заключается в контекстно-зависимой интерпретации.
IMSoP
@IMSoP - я уже редактировал ответ до того, как вы разместили комментарий. :)
Spudley
1
Он по-прежнему говорит: «{} [true] на самом деле вообще недействителен» в самом начале ответа.
IMSoP
Кроме того, OP не сказал « {}[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.whatever)) = LogicalNOT (convertToBool (undefined)) = LogicalNOT (false) = true
  • ({}['whatever']) = Группировка (NewObject.whatever) = Группировка (undefined) = undefined
Бен Леш
источник
8

Это происходит потому, что {}в вашем понимании это не буквальное представление Object, а пустая область видимости (или пустой блок кода):

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

Он просто оценивает код внутри области видимости, а затем показывает ваш массив.

И от твоего

!{}[true]

Просто преобразует эту область в int и возвращает тот же массив true. В этом коде нет логических проверок.

И если вы попытаетесь проверить результат, {}[true]вы получите false:

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

Поскольку больше нет размаха.

Итак, !в своем вопросе сделайте то же самое:

!function() {
   //...
}
antyrat
источник
Это будет легче увидеть, если вы это сделаете var x = {}; x[true].
Крис Хейс
1
Я не уверен, что вы имеете в виду под «преобразует в int в этой области»; Я думаю , что с ведущим !это будет истолковано как пустой объект, а не сферы, и это несоответствие.
IMSoP
6
  • {} это объект без свойств.
  • Поскольку []непосредственно следует за объектом, это означает «Доступ к свойству с этим именем», а не «Создать массив».
  • trueявляется логическим, но используется как имя свойства, поэтому преобразуется в строку ( "true")
  • У объекта нет вызываемого свойства true(поскольку у него нет свойств), поэтому {}['true']онundefined
  • !undefinedбросает undefinedв булево ( false)
  • Оператор not превращается falseв true.
Quentin
источник
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]в консоль Developer Tools (или 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оператора ??

... (?????)

Это оператор метки , вы уже видели его раньше ... Он используется в циклах и in 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]

Давайте разделим вещи здесь:

а) Делая отрицание, мы информируя анализатор , что {}является объектом .

б) Как показано в примере 2 , {}объект не имеет вызываемого свойства true, поэтому это выражение будет оцениваться как undefined.

в) Конечный результат - отрицание undefinedценности. Javascript выполняет неявное преобразование типов , а undefinedзначение является ложным .

г) Итак, отрицание false... true!

Алсидес Кейруш Агиар
источник