为什么在JavaScript中将{} 评估为true?
{}[true]
是[true]
, ![true]
应该是false
。
那么为什么!{}[true]
评估为true
?
我相信那是因为plain {}[true]
被parsing为一个空语句块(不是对象字面量),后面跟着一个包含true
的数组,这是true
。
另一方面,应用!
运算符使parsing器将{}
解释为对象字面量,所以下面的{}[true]
成为一个成员访问,返回undefined
,并且!{}[true]
确实为true
(如!undefined
为true
)。
由于{}[true]
不返回true
,但undefined
,并且undefined
被评估为false
:
'use strict'; var b = {}[true]; alert(b); // undefined b = !{}[true]; alert(b); // true
因为
{}[true]
评估为undefined
, !undefined
为true
。
来自@schlingel:
true
用作键和{}
作为哈希映射。 不存在一个true
的属性,所以它返回undefined
。 不出所料,如预期的那样。
控制台会话( Node.js [0.10.17]
):
> {}[true] undefined > !{}[true] true > [true] [ true ] > ![true] false >
但是,在Google Chrome控制台中:
> !{}[true] true
所以,没有矛盾。 您可能正在使用旧版本的JavaScript VM。 对于需要进一步证据的人员:
UPDATE
随着Firefox ,它也评估为true
:
造成混淆的原因是误解了你的第一个主张:
{}[true]
是[true]
你运行它时看到的是一个模糊的结果。 Javascript有一套定义的规则来处理这种歧义,在这种情况下,它会把你看到的一个signle语句分解成两个单独的语句。
所以Javascript将上面的代码视为两个单独的语句:首先是{}
,然后是完全独立的[true]
。 第二个陈述是什么给你的结果[true]
。 第一个陈述{}
完全被忽略。
你可以通过尝试以下方法来certificate这一点:
({}[true])
即把整个事情都用括号括起来,迫使解释者把它看作一个单一的陈述。
现在你会看到你的语句的实际值是undefined
。 (这也将帮助我们以后了解下一部分)
现在我们知道你的问题的最初部分是一个红色的鲱鱼,所以让我们来看看问题的最后部分:
那么为什么!{} [true]评估为真?
在这里,我们有同样的说法,但是有一个!
附加到它的前面。
在这种情况下,Javascript的规则告诉它评估整个事情作为一个单一的声明。
回头看当我们把前面的陈述包装在括号里时发生了什么; 我们没有undefined
。 这一次,我们正在有效地做同样的事情,但把一个!
在它前面。 所以你的代码可以简化为!undefined
,这是true
。
希望这个解释一下。
这是一个复杂的野兽,但在这里学习的教训是在控制台评估它们时使用括号括起来,以避免这样的假结果。
{}[true]
是undefined
。 要find这样写:
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; }
所以基本上,它用expression式在对象上执行一个
call
。 expression是:
with ((window && window.console && window.console._commandLineAPI) || {}) { {}+{};// <-- This is your code }
所以,正如你所看到的,expression式正在被直接评估,没有包装的括号。
更多的信息可以在这个问题中find。
这里的答案很好,下面是伪代码的细节:
-
{}['whatever']
=空块,NewArray('whatever')= NewArray('whatever') -
{}[true]
=空块,NewArray(true)= NewArray(true) - LogicalNOT(convertToBool(NewObject.whatever))= LogicalNOT(convertToBool(undefined))= LogicalNOT(false)= true
-
({}['whatever'])
=分组(NewObject.whatever)=分组(未定义)=未定义
发生这种情况是因为{}
在您的意义上不是Object
文字表示,而是空的范围(或空代码块):
{ var a = 1 }[true] // [true] (do the same thing)
它只是评估范围内的代码,然后显示你的数组。
从你的
!{}[true]
只是转换为int这个范围,并返回相同的数组true。 在这个代码中没有布尔检查。
如果您尝试检查来自{}[true]
结果,您将得到您的false
:
{}[true] -> [true] -> ![true] -> false
因为没有更多的范围。
所以!
在你的问题做相同的:
!function() { //... }
-
{}
是没有属性的对象。 - 由于
[]
紧跟在对象之后,因此意味着“访问此名称的属性”而不是“创build数组” -
true
是一个布尔值,但被用作属性名称,所以它被转换为一个string("true"
) - 该对象没有名为
true
的属性(因为它没有属性),所以{}['true']
是undefined
-
!undefined
强制转换为布尔型(false
) - 不是操作员变成
true
。
你没有扭转它的价值。
![true] != [!true]
看看这个: 为什么是真的? '假':'真'返回'真'?
让我们再玩一点!
首先,让我们玩得开心!
//----------#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)这里, {}
被parsing为一个空的代码块。 如果没有赋值,否定,分组(使用圆括号)或者任何向parsing器指示this {}
是对象字面值的语法,默认的假设就是认为它只是一个无用的空白块。
这是这种行为的certificate:
{ alert(123) }[true]
上面的代码将正常显示警报,并且将以与[true]
相同的方式评估为[true]
。
没有分号的块语句
后面的块types语句不需要分号。
例如:
for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")
显示两个警报。
所以,我们可以看到,一个没有分号的空块语句是有效的,而且什么都不做。 这样,当您在Developer Tools(或Firebug)控制台中input{}[true]
时,评估值将是最后一个expression式语句的值。 在这种情况下,最后一个expression式语句是[true]
。
2)在赋值语境中,parsing器将确保{}
是一个对象字面量。 当你做var a = {}[true]
,你删除了任何不明确的地方,并且closures了parsing器, {}
不是块语句。
所以,在这里,你试图从一个空对象中获得一个"true"
的键值。 显然,这个键名没有键值对。 这样,一个variables是未定义的。
保留字作为对象键
ECMAScript 5允许对象键为保留字。 所以,下面的键是合法的:
var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}
3)与例1相同的解释。 但是…如果{ b: 12345 }
部分被视为块语句, b: 12345
语句的types是什么?
…(?????)
这是一个标签声明 ,你已经看到它之前…它用于循环和switch
。 下面是关于标签语句的一些有趣的链接: 1 ,(2)[ 在Javascript中嵌套循环的最佳方式是什么? ,(3)[ 如何在JavaScript中打破嵌套循环? 。
注意:只要尝试评估一下:
{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :
标签语句不能用逗号分隔符分隔,您需要用分号分隔它们。 所以这是有效的: {a: 1; b: 2}
{a: 1; b: 2}
4)请参阅示例1和3的解释…
5)再一次,我们将一个{ b: 12345 }
当作一个代码块来对待,并且您正尝试使用点符号来访问一个代码块的属性,显然,这是不允许的,parsing器抛出一个"Unexpected token :"
exception。
6)代码与上面的例子几乎相同,但是通过用expression式分组操作符围绕{ b: 12345 }
语句,分析器将知道这是一个对象。 这样,您就可以正常访问"b"
属性。
7)记住例2 ,我们在这里有一个赋值,parsing器知道{ b: 12345 }
是一个对象。
8)与上面的例子相同,而不是点符号,这里我们使用括号表示法 。
9)我已经说过,块语句中的这个"identifier: value"
语法是一个标签。 但是,您还必须知道标签名称不能是保留关键字(与对象属性名称相反)。 当我们试图定义一个名为"true"
的标签时,我们得到了一个SyntaxError
。
10)同样,我们正在处理一个对象。 在这里使用保留字没有问题。 =)
11)最后,我们有这个: !{}[true]
让我们分开这里的东西:
a)通过否定,我们通知parsing器{}
是一个对象 。
b)如示例2所示, {}
对象没有名为true
的属性,因此此expression式将评估为undefined
。
c)最后的结果是否定undefined
值。 Javascript执行隐式types转换 ,而undefined
值是falsy 。
d)所以,否定false
是true
!