What is block inside [[scope]] key and when it is created?

Viewed 89

I tried the following code

let let_arr = [];
for (let i = 0; i < 4; i++) {
  let_arr[i] = () => {
    return {
        let_i: i
    }
  };
}
console.log(let_arr);

and I found the block key exists when we use let in for loop.

[ƒ, ƒ, ƒ, ƒ]
  0: () => {…}
    arguments: (...)
    caller: (...)
    length: 0
    name: ""__proto__: ƒ ()
    [[FunctionLocation]]: iif.js:26
    [[Scopes]]: Scopes[3]
        0: Block {i: 0}
        1: Script {let_arr: Array(4), var_arr: Array(4)}
        2: Global {window: Window, self: Window, document: document, name: "", location: Location, …}

While when using var for (var i = 0; i < 4; i++) block element is missing.

[[Scopes]]: Scopes[2]
    0: Script {let_arr: Array(4), var_arr: Array(4)}
    1: Global {window: Window, self: Window, document: document, name: "", location: Location, …}
2 Answers

This is the expected behaviour, because there are no variables in the block scope if you declare your variable with var. It's going to be hoisted up to the global scope in this case. You can read more about block scope here.

Javascript functions are actually closures, that is, when you save a function somewhere, not only its code is retained, but also all variables available at the creation time. Variables are stored in dictionaries called scopes, which can be nested in each other ("scope chain").

In order to conserve memory, the engine only retains scopes that are actually used in the function. If a scope does not contain let/const, it is considered unused and omitted. If a scope does contain let, but that variable is not used in the function, it's also omitted. Otherwise the scope is retained and attached to the closure.

A couple of illustrations:

var f

{
    var a
    f = () => a   // Global
}
console.dir(f)

//

{
    let a
    f = () => a
}
console.dir(f) // Block a + Global

//

{
    let a
    {
        let b
        {
            let c
            f = () => b  
        }
    }
}
console.dir(f)  // Block b + Global

//


{
    let a
    {
        let b
        {
            let c
            f = () => eval("b")
        }
    }
}
console.dir(f) // 'eval' retains everything

Related