Why does !{}[true] evaluate to true in JavaScript?

前端 未结 10 1460
一个人的身影
一个人的身影 2020-12-12 17:55

{}[true] is [true] and ![true] should be false.

So why does !{}[true] evaluate to true

相关标签:
10条回答
  • 2020-12-12 18:08

    Let's Play a Little More!

    First, let's have some fun!:

    //----------#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
    

    Ok, let's try to understand these crazy behaviors, one by one:

    1) Here, the {} is parsed as an empty code block. Without an assign, negation, grouping (with parentheses) or any syntax which indicates to the parser that this {} is an object literal, the default assumption is to think it is simply a useless empty block.

    This is a proof of this behavior:

    { alert(123) }[true]
    

    The code above will show the alert normally, and will be evaluated as [true], in the same way {}[true] is.

    Block Statements Without Semicolons

    A block-type statement doesn't need a semicolon after it.

    For instance:

    for(var i=0; i < 1; i++){}function a(){};alert("Passed here!");if(true){}alert("Passed here too!")
    

    Both alerts are shown.

    So, we can see that an empty block statement, without a semicolon, is valid and simply does nothing. This way, when you enter {}[true] in the Developer Tools (or Firebug) Console, the evaluated value will be the value of the last expression statement. In this case, the last expression statement is [true].

    2) In an assignment context, the parser will make sure that {} is an object literal. When you do var a = {}[true], you remove any ambiguity and tip the parser off that {} is not a block statement.
    So, here, you're trying to get a value with a key "true" from an empty object. Obviously, there's no key-value pair with this key name. This way, the a variable is undefined.

    Reserved words as Object keys

    ECMAScript 5 allows object keys to be reserved words. So, the following keys are legal:

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

    3) The same explanation of example 1. But... If the { b: 12345 } part is treated as a block statement, what's the type of the b: 12345 statement??

    ... (?????)

    It's a label statement, you already saw it before... It's used in loops and in switch. Here are a few interesting links about label statements: 1, (2)[Best way to break from nested loops in Javascript?, (3)[How to break nested loops in javascript?.

    NOTE: Just try to evaluate this:

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

    Label statements can't be separeted by the comma operator, you would need to separate them with a semicolon. So this is valid: {a: 1; b: 2}

    4) See the explanations for the examples 1 and 3...

    5) One more time, we have a { b: 12345 } being treated as a code block, and you're trying to access a property of a code block by using the dot notation, and obviously, this is not allowed, and the parser throws an "Unexpected token :" exception.

    6) The code is almost identical to the above example, but by surrounding the { b: 12345 } statement with the expression grouping operator, the parser will know that is an object. This way, you'll be able to access the "b" property normally.

    7) Remember the example 2, we have an assignment here, the parser knows that { b: 12345 } is an object.

    8) Identical to the above example, but instead of the dot notation, here we're using the bracket notation.

    9) I already said that this "identifier: value" syntax inside a block statement is a label. But, you also have to know that a label name can't be a reserved keyword (the opposite of object property names). When we tried to define a label called "true", we got a SyntaxError.

    10) Again, we're dealing with an object. No problems using reserved words here. =)

    11) Finally, we have this: !{}[true]

    Let's separate the things here:

    a) By doing a negation, we're informing to the parser that the {} is an object.

    b) As shown in the example 2, a {} object doesn't have a property called true, so this expression will evaluate to undefined.

    c) The final result is the negation of undefined value. Javascript performs implicity type conversion, and undefined value is falsy.

    d) So, the negation of false is... true!

    0 讨论(0)
  • 2020-12-12 18:11

    You're not reversing the value of it.

    ![true] != [!true]
    

    Check this out: Why is !true ? 'false' : 'true' returning 'true'?

    0 讨论(0)
  • 2020-12-12 18:17

    {}[true] is undefined. To find that write this:

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

    or simply:

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

    We know that !undefined is true.


    From @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;
      }
    

    So basically, it performs a call on the object with the expression. The expression being:

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

    So, as you can see, the expression is being evaluted directly, without the wrapping parenthesis.

    More information can be found in this question.

    0 讨论(0)
  • 2020-12-12 18:19

    This happens because {} in your meaning is not literal presentation of Object, but empty scope ( or empty code block ):

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

    It just evaluates code inside scope and then shows you your array.

    And from your

    !{}[true]
    

    Just converts to int this scope and return same array true. There is no bool checks in this code.

    And if you will try to check result from {}[true] you will get your false:

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

    As there is no more any scope.

    So ! in your question do the same as:

    !function() {
       //...
    }
    
    0 讨论(0)
  • 2020-12-12 18:20
    • {} is an object with no properties.
    • Since [] immediately follows an object, it means "Access a property of this name" and not "Create an array"
    • true is a boolean, but is being used as an property name so it is cast to a string ("true")
    • The object does not have a property called true (since it has no properties) so {}['true'] is undefined
    • !undefined casts undefined to a boolean (false)
    • The not operator turns false into true.
    0 讨论(0)
  • 2020-12-12 18:22

    The answers here are good, here's a breakdown in pseudo-code:

    • {}['whatever'] = empty block, NewArray('whatever') = NewArray('whatever')
    • {}[true] = empty block, NewArray(true) = NewArray(true)
    • !{}['whatever'] = LogicalNOT(convertToBool(NewObject.whatever)) = LogicalNOT(convertToBool(undefined)) = LogicalNOT(false) = true
    • ({}['whatever']) = Grouping(NewObject.whatever) = Grouping(undefined) = undefined
    0 讨论(0)
提交回复
热议问题