为什么!{} [true]在JavaScript中评估为true?


131

{}[true][true]并且![true]应该是false

那么为什么要 !{}[true]评估true呢?


30
var o = {}; o[true] === undefined
azz

2
此处的解释可能与之前的问题
IMSoP,2013年

45
“因为Java语言很愚蠢”可能不是您要寻找的答案。
乔治,

2
如前所述,如果您{}[true] === [true]要从控制台获取,那是因为它被{}视为空代码块,而不是对象。
azz

3
如果它可以帮助,尝试比较{},并({})在控制台(或{}[true]({})[true])。同样,正如没有人提到的那样,object [true]被评估为object [“ true”]。
BiAiB

Answers:


172

我认为这是因为plain {}[true]被解析为一个空语句块(不是对象文字),后跟一个包含的数组true,该数组是true

在另一方面,在施加!操作者使解析器解释{}作为对象常量,所以下面{}[true]成为成员访问该回报undefined,并且!{}[true]确实是true(作为!undefinedtrue)。


25
另一方面,!undefined是正确的事实仍然不可原谅。
evilcandybag

87
@evilcandybag:绝对不是。 undefined是虚假的(我们经常依赖的东西if (obj.maybeExists) ...),因此这在逻辑上是!undefined完全正确的。
2013年

8
@Josh,我认为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

21
如果{}[true]在控制台中求值,则会得到[true],因为会{}被解释为空代码块,而不是对象。都是关于的上下文和含糊之处{}
IMSoP

1
@IMSoP但是为什么{key:"value"}[1,2,3];也要评估[1,2,3]
t.niese,

3
@ t.niese,因为它被解析为一个语句块,其中包含标签(key:)和字符串文字("value"),后跟数组。解析器仍然看不到对象文字。
弗雷德里克·哈米迪

1
@FrédéricHamidi啊,就是这样。我压制了标签^^
t.niese 2013年

1
@dooxe阅读其他答案;这全都与解释它的上下文有关。如果将其包装在alert()或中console.log(),或将其分配给变量,则将更改上下文,这就是为什么它的行为与在控制台中单独键入的行为不同的原因。
IMSoP 2013年

27

因为

{}[true]

评估为undefined!undefinedtrue

来自@schlingel:

true用作键和{}哈希图。该键不存在一个属性,true因此它返回undefined。不undefinedtrue,符合市场预期。

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

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

但是,在Google Chrome控制台中:

> !{}[true]
true

因此,没有矛盾。您可能正在使用旧版本的JavaScript VM。对于那些需要进一步证据的人:

在此处输入图片说明

更新

使用Firefox时,其评估结果为true

在此处输入图片说明


如果您执行此操作eval('{}[true]')或将其键入控制台,则不会。然后,例如als {}"test"test,甚至{key:"value"}"test"test
t.niese,

有趣的是,您在哪个js引擎中进行测试?
t.neese,

@ t.niese我刚刚将其输入到节点控制台中,这就是我得到的。
Games Brainiac

只是出于好奇。{}[true];(和;)是否会[true]为您返回,因为在此返回?
t.neese,

2
选男人的理由?有一个几乎相同的答案,这一个有8票,我也得到了downvote?我做错什么了?
Games Brainiac

23

造成混淆的原因是您对第一个主张的误解:

{}[true][true]

运行时所看到的是模棱两可的结果。JavaScript对于如何处理此类歧义性具有一组已定义的规则,在这种情况下,它会将您看到的signle语句分解为两个单独的语句。

因此,JavaScript将以上代码视为两个单独的语句:首先,有一个{},然后有一个完全分开的[true]。第二个陈述是给您结果的[true]{}实际上,第一个陈述被完全忽略了。

您可以尝试以下方法证明这一点:

({}[true])

例如,将整个内容包装在方括号中,以迫使解释器将其作为单个语句读取。

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

现在我们知道您问题的开始部分是一条红鲱鱼,所以让我们进入问题的最后部分:

那么,为什么{{} [true]评估为true?

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

在这种情况下,Javascript的规则告诉它以单个语句来评估整个事物。

回顾一下我们将前面的语句放在方括号中时发生的情况;我们得到了undefined。这次,我们实际上在做相同的事情,但是要放在!前面。所以,你的代码可以作为简化!undefined,这是true

希望这可以解释一下。

这是一个复杂的野兽,但是要在这里学习的课程是在控制台中评估语句时在语句周围使用方括号,以避免出现类似错误的结果。


2
我不认为确切{}[true]无效的,只是模棱两可。可以解释为“空代码块后跟数组文字”或“没有属性的对象文字,正在访问其属性”。我不知道从技术上讲第一个是否是ASI(无论如何,许多语言都不会在其中添加分号),但这是上下文相关的解释是问题的核心。
IMSoP

@IMSoP-在发布评论之前,我已经编辑了答案。:)
Spudley

1
答案刚开始时,仍然说“ {} [true]实际上根本无效”。
IMSoP 2013年

此外,OP没有说“ {}[true]true”,他们说“ {}[true][true]”,这是支支吾吾的两个有效的解释之一。
IMSoP 2013年

14

{}[true]undefined。为了找到这个,写这个:

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

或者简单地:

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

我们知道那!undefinedtrue


来自@Benjamin Gruenbaum的回答

Chrome dveloper工具执行以下操作

  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使用表达式在对象上执行a 。表达式为:

with ((window && window.console && window.console._commandLineAPI) || {}) {
    {}+{};// <-- This is your code
}

因此,如您所见,表达式是直接求值的,而没有换行括号。

此问题中可以找到更多信息。


10

这里的答案很好,这是伪代码的细分:

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

8

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

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

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

从你的

!{}[true]

只是将这个范围转换为int并返回相同的数组true。此代码中没有布尔检查。

如果您尝试检查结果,{}[true]您将得到false

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

由于没有任何范围。

因此,!在您的问题中,请执行以下操作:

!function() {
   //...
}

如果这样做,则更容易看到var x = {}; x[true]
克里斯·海斯

1
我不确定“转换为该范围”是什么意思;我认为,与领先的!解释为一个空的对象,而不是范围,这就是差距。
IMSoP

6
  • {} 是没有属性的对象。
  • 由于[]紧随对象,因此表示“访问此名称的属性”而不是“创建数组”
  • true是一个布尔值,但被用作属性名称,因此将其强制转换为字符串("true"
  • 对象不具有一个名为属性true(因为它没有属性),所以{}['true']undefined
  • !undefined转换undefined为布尔值(false
  • 在没有操作者接通falsetrue

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

显示两个警报。

因此,我们可以看到没有分号的空的block语句是有效的,并且什么也不做。这样,当您{}[true]在开发人员工具(或Firebug)控制台中输入时,评估值将是最后一个expression语句的值。在这种情况下,最后一个表达式语句是[true]

2)在分配上下文中,解析器将确保这{}是一个对象文字。当执行var a =时{}[true],您消除了任何歧义,并提示了{}不是block语句的解析器。
因此,在这里,您尝试"true"从空对象获取带有键的值。显然,这个键名没有键值对。这样,a变量是不确定的。

保留字作为对象键

ECMAScript 5允许对象键为保留字。因此,以下密钥是合法的:

var obj = {if: 111, for: 222, switch: 333, function: 444, true: 555}

3)与示例1相同的解释。但是...如果将该{ b: 12345 }零件视为一个块语句,该b: 12345语句的类型是什么?

...(??????)

这是一个标签声明,您之前已经看过了...在循环和循环中使用switch。以下是一些有关标签语句的有趣链接:1,(2)[ 打破Java嵌套循环的最佳方法?,(3)[ 如何打破javascript中的嵌套循环?

注意:只需尝试评估此:

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

标签语句不能用逗号分隔,您需要用分号将它们分开。所以这是有效的:{a: 1; b: 2}

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

5)再有一次,我们{ b: 12345 }被视为代码块,并且您正尝试使用点表示法来访问代码块的属性,显然,这是不允许的,并且解析器会引发"Unexpected token :"异常。

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

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

8)与以上示例相同,但这里使用的是括号符号,而不是点符号

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

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

11)最后,我们有:!{}[true]

让我们在这里将它们分开:

a)通过否定,我们通知解析器the {}一个object

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

c)最终结果是undefined价值否定。Javascript执行隐式类型转换,并且undefined值是falsy

d)因此,否定false是... true

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.