【问题标题】:Why does !{}[true] evaluate to true in JavaScript?为什么 !{}[true] 在 JavaScript 中评估为 true?
【发布时间】:2013-11-11 05:45:13
【问题描述】:

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

那么为什么!{}[true] 的计算结果为true

【问题讨论】:

  • var o = {}; o[true] === undefined.
  • 这里的解释很可能与this previous question上讨论的怪事非常相似
  • “因为 Javascript 很傻”可能不是您要寻找的答案。
  • 如前所述,如果您从控制台获取{}[true] === [true],那是因为它将{} 视为一个空代码块,而不是一个对象。
  • 如果有帮助,请尝试在您的控制台中比较{}({})(或{}[true]({})[true])。此外,正如没有人提到的那样,object[true] 被评估为 object["true"]。

标签: javascript


【解决方案1】:

因为{}[true]不返回true,而是undefined,而undefined被评估为false

http://jsfiddle.net/67GEu/

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

【讨论】:

  • 如果您在控制台中评估{}[true],您会得到[true],因为{} 被解释为一个空代码块,而不是一个对象。这完全取决于{} 的上下文和模糊性。
  • @IMSoP 但为什么 {key:"value"}[1,2,3]; 也评估为 [1,2,3]
  • @t.niese,因为它被解析为一个语句块,其中包含一个标签 (key:) 和一个字符串文字 ("value"),后跟一个数组。解析器仍然看不到对象字面量。
  • @FrédéricHamidi 是的,就是这样。我压制标签^^
  • @dooxe 阅读其他答案;这完全取决于解释它的上下文。如果将其包装在 alert()console.log() 中,或者将其分配给变量,则您正在更改上下文,这就是为什么它的行为方式与在控制台中单独键入不同的原因。
【解决方案2】:

因为

{}[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]') 或将其输入控制台,则不会。然后例如也{}"test"test 甚至{key:"value"}"test"test
  • 有意思,你是在哪个js引擎上测试的?
  • @t.niese 我刚刚在我的节点控制台中输入了它,这就是我得到的。
  • 只是为了好奇。 {}[true];(带有;)是否会为您返回[true],因为它在这里吗?
  • 投反对票的理由?这个答案有一个几乎相同的答案,有 8 票,我得到了否决票?我做错了什么?
【解决方案3】:

你并没有颠倒它的价值。

![true] != [!true]

看看这个:Why is !true ? 'false' : 'true' returning 'true'?

【讨论】:

    【解决方案4】:
    • {} 是一个没有属性的对象。
    • 由于[] 紧跟在一个对象之后,它的意思是“访问同名的属性”而不是“创建一个数组”
    • true 是一个布尔值,但被用作属性名称,因此它被转换为字符串 ("true")
    • 该对象没有名为true 的属性(因为它没有属性)所以{}['true']undefined
    • !undefinedundefined 转换为布尔值 (false)
    • not 运算符将false 转换为true

    【讨论】:

    • 对于{}[true](没有其他上下文),{}不是一个没有属性的对象,它是一个空代码块。
    【解决方案5】:

    发生这种情况是因为您所说的 {} 不是 Object 的字面表示,而是空范围(或空代码块):

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

    它只是评估作用域内的代码,然后显示你的数组。

    从你的

    !{}[true]
    

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

    如果您尝试检查来自{}[true] 的结果,您将获得您的false

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

    因为没有更多的范围了。

    所以! 在你的问题中做同样的事情:

    !function() {
       //...
    }
    

    【讨论】:

    • 如果你这样做var x = {}; x[true]会更容易看到。
    • 我不确定您所说的“转换为 int 这个范围”是什么意思;我认为与领先的!解释为一个空对象,而不是范围,这就是差异。
    【解决方案6】:

    我相信这是因为普通的 {}[true] 被解析为一个空语句块(不是对象字面量),后跟一个包含 true 的数组,即 true

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

    【讨论】:

    • 另一方面,!undefined 为真这一事实仍然不可原谅。
    • @evilcandybag:绝对不是。 undefined 是假的(我们经常依赖的东西 -- if (obj.maybeExists) ...),所以!undefined 是真的很符合逻辑。
    • @Josh,我认为 evilcandybag 在某些语言中更喜欢类似于null 的行为,!undefined 等于undefined。但是,在 Javascript 中情况并非如此。
    • @evilcandybag:因此必须定义 not undefined (!undefined) 的东西才合乎逻辑。如果定义了某些东西,那么它通常被解释为true
    • @Cruncher 如果 a 未定义,b 未定义,我们怎么可能知道 a != b?特别是当两个变量的唯一已知特征完全相同时。
    【解决方案7】:

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

    {}[true][true]

    您在运行它时看到的内容是模棱两可的结果。 Javascript 有一套关于如何处理此类歧义的定义规则,在这种情况下,它会将您看到的单一语句分解为两个单独的语句。

    所以 Javascript 将上述代码视为两个独立的语句:首先,有一个 {},然后有一个完全独立的 [true]。第二个语句是什么给你结果[true]。第一条语句{} 被完全忽略。

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

    ({}[true])
    

    即将整个内容放在括号中以强制解释器将其作为单个语句读取。

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

    现在我们知道您问题的开头部分是一个转移话题,所以让我们进入问题的最后部分:

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

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

    在这种情况下,Javascript 的规则告诉它把整个事情作为一个单独的语句来评估。

    回想一下我们把前面的语句用括号括起来时发生的事情;我们得到了undefined。这一次,我们实际上是在做同样的事情,但在它前面放了一个!。所以你的代码可以简化为!undefined,也就是true

    希望这能解释一下。

    这是一个复杂的野兽,但这里要吸取的教训是在控制台中评估语句时使用括号括起来,以避免这样的虚假结果。

    【讨论】:

    • 我不认为{}[true] 完全无效,只是模棱两可。它可以被解释为“空代码块后跟数组文字”或“没有属性的对象文字,其中一个属性正在被访问”。我不知道第一个在技术上是否是 ASI 的情况(许多语言无论如何都不会在其中放置分号),但问题的核心是上下文相关的解释。
    • @IMSoP - 在您发表评论之前,我已经编辑了答案。 :)
    • 它仍然在答案的开头说“{}[true] 实际上根本不有效”。
    • 另外,OP 没有说“{}[true]true”他们说“{}[true][true]”,这是对模棱两可的陈述的两种有效解释之一。
    【解决方案8】:

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

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

    或者简单地说:

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

    我们知道!undefinedtrue


    来自@Benjamin Gruenbaum's answer

    Chrome dveloper tools does the following:

      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
    }
    

    因此,如您所见,表达式是直接计算的,没有括号。

    更多信息可以在this question找到。

    【讨论】:

      【解决方案9】:

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

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

      【讨论】:

        【解决方案10】:

        让我们再玩一点!

        首先,让我们玩得开心!

        //----------#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)参见示例13的解释...

        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 conversionundefined value is falsy

        d) 所以,false 的否定是...true

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-17
          • 2013-09-02
          • 1970-01-01
          • 2015-03-15
          • 2019-06-30
          • 2012-01-24
          • 2014-02-16
          • 2020-08-15
          相关资源
          最近更新 更多