Why does !{}[true] evaluate to true in JavaScript?

{}[true] is [true] and ![true] should be false.

So why does !{}[true] evaluate to true?

6879 次浏览

因为 {}[true]不返回 true,而是返回 undefined,并且 undefined被计算为 false:

Http://jsfiddle.net/67geu/

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

因为

{}[true]

计算结果为 undefined,而 !undefinedtrue

来自@schlingel:

true用作键,{}用作哈希映射。不存在具有键 true的属性,因此它返回 undefined。不像预期的那样,undefinedtrue

控制台会话(Node.js [0.10.17]) :

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

然而,在 谷歌浏览器控制台中:

> !{}[true]
true

所以,不要自相矛盾。你可能使用的是旧版本的 JavaScript 虚拟机。对于那些需要进一步证据的人:

Enter image description here

更新

对于 火狐,它的评估结果也是 true:

Enter image description here

  • {}是没有属性的对象。
  • 因为 []紧跟在一个对象后面,所以它的意思是“访问这个名称的属性”,而不是“创建一个数组”
  • true是一个布尔值,但被用作属性名,因此它被强制转换为字符串("true")
  • 该对象没有名为 true的属性(因为它没有属性) ,所以 {}['true']undefined
  • !undefinedundefined强制转换为布尔值(false)
  • Not 操作符将 false转换为 true

发生这种情况是因为 {}在您的意思中不是 Object的字面表示,而是空作用域(或空代码块) :

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

它只是计算范围内的代码,然后向您显示数组。

还有你的

!{}[true]

只是转换为 int 这个范围并返回相同的数组 true。

如果你尝试从 {}[true]检查结果,你会得到你的 false:

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

因为没有更多的范围。

因此,!在你的问题中的作用与:

!function() {
//...
}

我相信这是因为纯 {}[true]被解析为一个空语句块(而不是对象文字) ,后面跟着一个包含 true的数组,也就是 true

另一方面,应用 !操作符使解析器将 {}解释为对象文字,因此以下 {}[true]成为返回 undefined的成员访问,而 !{}[true]确实是 true(因为 !undefinedtrue)。

造成这种困惑的原因是对你第一个主张的误解:

{}[true][true]

当你运行它的时候,你所看到的是一个模糊的结果。Javascript 对于如何处理这样的歧义有一组已定义的规则,在本例中,它将您所看到的单语句分解为两个独立的语句。

因此 Javascript 将上面的代码看作是两个独立的语句: 首先,有一个 {},然后有一个完全独立的 [true]。第二个语句是什么给你的结果 [true]。第一个语句 {}实际上被完全忽略。

你可以通过以下方法来证明这一点:

({}[true])

即用括号将整个内容包装起来,以强制解释器将其作为单个语句读取。

现在您将看到语句的实际值是 undefined。(这也将有助于我们以后理解下一部分)

现在我们知道你的问题的开始部分是在转移注意力,所以让我们进入问题的最后部分:

那么为什么! {}[ true ]计算为真?

在这里,我们有相同的语句,但是前面附加了一个 !

在这种情况下,Javascript 的规则告诉它将整个事情作为一个单独的语句进行计算。

回过头来看看我们在括号中包装前面的语句时发生了什么; 我们得到了 undefined。这一次,我们实际上是在做同样的事情,但是把一个 !放在它的前面。因此,您的代码可以简化为 !undefined,即 true

希望这能解释一下。

这是一个复杂的问题,但是这里要学习的教训是在控制台中评估语句时使用括号,以避免出现这样的虚假结果。

{}[true]undefined。要找到它,请写下:

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

或者简单地说:

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

我们知道 !undefinedtrue


来自 @ 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
}

所以,正如您所看到的,表达式是直接求值的,没有包装括号。

更多信息可以在 这个问题中找到。

这里的答案很好,下面是伪代码的分解:

  • {}['whatever'] = 空块,NewArray (‘ whatever’) = NewArray (‘ whatever’)
  • {}[true] = 空块,NewArray (true) = NewArray (true)
  • !{}['whatever'] = LogicalNOT (ConvertToBool (NewObject.whatever)) = LogicalNOT (ConvertToBool (未定义)) = LogicalNOT (false) = true
  • ({}['whatever']) = 分组(NewObject.whatever) = 分组(未定义) = 未定义

让我们再玩一会儿!

首先,让我们找点乐子吧:

//----------#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!")

两个警报都会显示。

因此,我们可以看到一个没有分号的空块语句是有效的,并且什么也不做。这样,当您在开发工具(或 Firebug)控制台中输入 {}[true]时,评估的值将是最后一个 表达式语句的值。在本例中,最后一个表达式语句是 [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中使用。这里有一些关于 label 语句的有趣链接: 1,(2)[ 打破 Javascript 中嵌套循环的最佳方法?,(3)[ 如何打破 javascript 中的嵌套循环?

注意: 试着评估一下:

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

Label 语句不能由 逗号操作符分隔,需要用分号分隔它们。所以这是有效的: {a: 1; b: 2}

4) 参见示例 13的说明..。

5) 再一次,我们有一个 { b: 12345 }被当作一个代码块,你试图通过使用 点符号来访问一个代码块的属性,很明显,这是不允许的,解析器抛出一个 "Unexpected token :"异常。

6) 代码与上面的示例几乎相同,但是通过用 表达式分组运算符包围 { b: 12345 }语句,解析器将知道这是一个对象。这样,您就能够正常地访问 "b"属性。

7) 记住这个例子 2,我们在这里有一个赋值,解析器知道 { b: 12345 }是一个对象。

8) 与上面的例子相同,但是这里我们使用的是 括号符号,而不是点符号。

9) 我已经说过块语句中的这个 "identifier: value"语法是一个标签。但是,您还必须知道标签名称不能是保留关键字(与对象属性名称相反)。当我们试图定义一个名为 "true"的标签时,我们得到了一个 SyntaxError

10) 同样,我们正在处理一个对象,在这里使用保留字没有问题。 =)

最后,我们有这个: !{}[true]

让我们把事情分开来看:

A)通过做一个否定,我们告诉解析器 {}一个物体

B)如示例 2所示,{}对象没有名为 true的属性,因此这个表达式的计算结果为 undefined

C)最终的结果是 undefined值的否定。

D)所以,false的负数是... true