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

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

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

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

10条回答
  •  -上瘾入骨i
    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!

提交回复
热议问题