让我们再玩一点!
首先,让我们玩得开心!
//----------#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] 时,评估值将是最后一个 expression statement 的值。在这种情况下,最后一个表达式语句是[true]。
2) 在赋值上下文中,解析器将确保{} 是一个对象字面量。当您执行 var a = {}[true] 时,您消除了任何歧义并提示解析器 {} 不是块语句。
因此,在这里,您试图从一个空对象中获取一个带有键 "true" 的值。显然,这个键名没有键值对。这样,a 变量是未定义的。
保留字作为对象键
ECMAScript 5 允许对象键是保留字。因此,以下键是合法的:
var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}
3) 同例1的解释。但...
如果{ b: 12345 }部分被当作block语句,b: 12345语句的类型是什么??
... (?????)
这是一个label statement,您之前已经看过它...它用于循环和switch。以下是一些关于标签语句的有趣链接:1, (2)[Best way to break from nested loops in Javascript?, (3)[How to break nested loops in javascript?。
注意:尝试评估一下:
{a: 1, b: 2} //=>>>SyntaxError: Unexpected token :
标签语句不能由comma 运算符分隔,您需要用分号分隔它们。所以这是有效的:{a: 1; b: 2}
4)参见示例1和3的解释...
5) 还有一次,我们将 { b: 12345 } 视为代码块,而您正尝试使用 dot notation 访问代码块的属性,并且显然,这是不允许的,解析器会抛出"Unexpected token :" 异常。
6) 代码几乎与上面的示例相同,但是通过将{ b: 12345 } 语句与expression grouping operator 包围起来,解析器将知道这是一个对象。这样就可以正常访问"b"属性了。
7)记住例子2,我们这里有一个赋值,解析器知道{ b: 12345 }是一个对象。
8) 与上面的示例相同,但这里我们使用的是bracket notation,而不是点符号。
9) 我已经说过块语句中的这个"identifier: value" 语法是一个标签。但是,您还必须知道标签名称不能是保留关键字(与对象属性名称相反)。当我们尝试定义一个名为"true" 的标签时,我们得到了一个SyntaxError。
10) 同样,我们正在处理一个对象。在这里使用保留字没有问题。 =)
11) 最后,我们得到了这个:!{}[true]
让我们把这里的东西分开:
a) 通过否定,我们通知解析器{} 是一个对象。
b) 如示例 2 所示,{} 对象没有名为 true 的属性,因此该表达式的计算结果为 undefined。
c) 最终结果是undefined 值的否定。 Javascript 执行implicity type conversion 和undefined value is falsy。
d) 所以,false 的否定是...true!