{}[true] is [true] and ![true] should be false.
{}[true]
[true]
![true]
false
So why does !{}[true] evaluate to true?
!{}[true]
true
因为 {}[true]不返回 true,而是返回 undefined,并且 undefined被计算为 false:
undefined
Http://jsfiddle.net/67geu/
'use strict'; var b = {}[true]; alert(b); // undefined b = !{}[true]; alert(b); // true
因为
计算结果为 undefined,而 !undefined为 true。
!undefined
来自@schlingel:
true用作键,{}用作哈希映射。不存在具有键 true的属性,因此它返回 undefined。不像预期的那样,undefined是 true。
{}
控制台会话(Node.js [0.10.17]) :
[0.10.17]
> {}[true] undefined > !{}[true] true > [true] [ true ] > ![true] false >
然而,在 谷歌浏览器控制台中:
> !{}[true] true
所以,不要自相矛盾。你可能使用的是旧版本的 JavaScript 虚拟机。对于那些需要进一步证据的人:
对于 火狐,它的评估结果也是 true:
你不是在逆转它的价值。
![true] != [!true]
看看这个 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的? 为什么是真的?
[]
"true"
{}['true']
发生这种情况是因为 {}在您的意思中不是 Object的字面表示,而是空作用域(或空代码块) :
Object
{ var a = 1 }[true] // [true] (do the same thing)
它只是计算范围内的代码,然后向您显示数组。
还有你的
只是转换为 int 这个范围并返回相同的数组 true。
如果你尝试从 {}[true]检查结果,你会得到你的 false:
{}[true] -> [true] -> ![true] -> false
因为没有更多的范围。
因此,!在你的问题中的作用与:
!
!function() { //... }
我相信这是因为纯 {}[true]被解析为一个空语句块(而不是对象文字) ,后面跟着一个包含 true的数组,也就是 true。
另一方面,应用 !操作符使解析器将 {}解释为对象文字,因此以下 {}[true]成为返回 undefined的成员访问,而 !{}[true]确实是 true(因为 !undefined是 true)。
造成这种困惑的原因是对你第一个主张的误解:
{}[true]是 [true]
当你运行它的时候,你所看到的是一个模糊的结果。Javascript 对于如何处理这样的歧义有一组已定义的规则,在本例中,它将您所看到的单语句分解为两个独立的语句。
因此 Javascript 将上面的代码看作是两个独立的语句: 首先,有一个 {},然后有一个完全独立的 [true]。第二个语句是什么给你的结果 [true]。第一个语句 {}实际上被完全忽略。
你可以通过以下方法来证明这一点:
({}[true])
即用括号将整个内容包装起来,以强制解释器将其作为单个语句读取。
现在您将看到语句的实际值是 undefined。(这也将有助于我们以后理解下一部分)
现在我们知道你的问题的开始部分是在转移注意力,所以让我们进入问题的最后部分:
那么为什么! {}[ true ]计算为真?
在这里,我们有相同的语句,但是前面附加了一个 !。
在这种情况下,Javascript 的规则告诉它将整个事情作为一个单独的语句进行计算。
回过头来看看我们在括号中包装前面的语句时发生了什么; 我们得到了 undefined。这一次,我们实际上是在做同样的事情,但是把一个 !放在它的前面。因此,您的代码可以简化为 !undefined,即 true。
希望这能解释一下。
这是一个复杂的问题,但是这里要学习的教训是在控制台中评估语句时使用括号,以避免出现这样的虚假结果。
{}[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,表达式是:
call
with ((window && window.console && window.console._commandLineAPI) || {}) { {}+{};// <-- This is your code }
所以,正如您所看到的,表达式是直接求值的,没有包装括号。
更多信息可以在 这个问题中找到。
这里的答案很好,下面是伪代码的分解:
{}['whatever']
!{}['whatever']
({}['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语句的类型是什么?
{ b: 12345 }
b: 12345
... (?????)
这是一个 标签声明,你以前已经看过了... 它在循环和 switch中使用。这里有一些关于 label 语句的有趣链接: 1,(2)[ 打破 Javascript 中嵌套循环的最佳方法?,(3)[ 如何打破 javascript 中的嵌套循环?。
switch
注意: 试着评估一下:
{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :
Label 语句不能由 逗号操作符分隔,需要用分号分隔它们。所以这是有效的: {a: 1; b: 2}
{a: 1; b: 2}
4) 参见示例 1和 3的说明..。
5) 再一次,我们有一个 { b: 12345 }被当作一个代码块,你试图通过使用 点符号来访问一个代码块的属性,很明显,这是不允许的,解析器抛出一个 "Unexpected token :"异常。
"Unexpected token :"
6) 代码与上面的示例几乎相同,但是通过用 表达式分组运算符包围 { b: 12345 }语句,解析器将知道这是一个对象。这样,您就能够正常地访问 "b"属性。
"b"
7) 记住这个例子 2,我们在这里有一个赋值,解析器知道 { b: 12345 }是一个对象。
8) 与上面的例子相同,但是这里我们使用的是 括号符号,而不是点符号。
9) 我已经说过块语句中的这个 "identifier: value"语法是一个标签。但是,您还必须知道标签名称不能是保留关键字(与对象属性名称相反)。当我们试图定义一个名为 "true"的标签时,我们得到了一个 SyntaxError。
"identifier: value"
SyntaxError
10) 同样,我们正在处理一个对象,在这里使用保留字没有问题。 =)
最后,我们有这个: !{}[true]
让我们把事情分开来看:
A)通过做一个否定,我们告诉解析器 {}是 一个物体。
B)如示例 2所示,{}对象没有名为 true的属性,因此这个表达式的计算结果为 undefined。
C)最终的结果是 undefined值的否定。
D)所以,false的负数是... true!