Search code examples
scopevarletiife

a variable defined with let is not defined in a same scope IIFE


I have some code below. I hope it can log a and b correctly, but as the result, it logs a as 1, b is not defined with error:

Uncaught ReferenceError: b is not defined

function foo(){
    var a = 1
    let b = 2
    (function bar() {
        console.log(a)
        console.log(b)
    }())
}
console.log(foo());

If I change the code to make the bar as a function declaration, every thing is fine

function foo(){
    var a = 1
    let b = 2
    function bar() {
        console.log(a)
        console.log(b)
    }
    bar()
}
console.log(foo());

I know some thing about function scope, block scope, hoisting. But I really don't understand what happens make the b to be 'not defined'

All the code above was run in the Chrome 66's devTools


Solution

  • This is one of the reasons not to rely on Automatic Semicolon Insertion (ASI) unless you and every programmer who might conceivably work on the code are very clear on the rules for ASI.

    Your code is trying to call 2 as a function, passing in the result of calling your bar function as an argument, because the () around the following IIFE can be reasonably (by ASI rules) considered part of the let b = statement. Since calling bar happens before b is declared (it's not declared until after its initializer runs), you get the error.

    If we change the line breaks a bit you can see what's going on:

    function foo(){
        var a = 1
        let b = 2(function bar() {
            console.log(a)
            console.log(b)
        }());
    }
    console.log(foo());

    Adding the ; fixes it:

    function foo(){
        var a = 1;
        let b = 2;
        (function bar() {
            console.log(a);
            console.log(b);
        }());
    }
    console.log(foo());