You already know that JavaScript engines implement functional scope through variable environments, so how does ES6 implement block-level scope support on top of functional scope?


function foo(){
    var a = 1
    let b = 2
    {
      let b = 3
      var c = 4
      let d = 5
      console.log(a)
      console.log(b)
    }
    console.log(b) 
    console.log(c)
    console.log(d)
}   
foo()
Copy the code

The execution process is as follows

  • Compile and create the execution context

1. All variables declared by var inside the function are stored in the variable environment during compilation.

2. The variables declared by let are saved to the Lexical Environment during compilation.

3. Inside the function’s scoped block, variables declared by let are not stored in the lexical environment.

  • Continue executing code

Seen from the figure, when entering the function block, the scope of the scope block variables through the let statement, will be stored in a separate lexical environment area, the area of the variable does not affect the outside of the scope of variables, such as in the scope statement face the variable b, within the scope block b also declares the variables, When executed inside the scope, they all exist independently.

In fact, inside the lexical environment, a small stack structure is maintained. The bottom of the stack is the outermost variable of the function. After entering a scoped block, the variables inside the scoped block will be pushed to the top of the stack. When the scope execution is complete, the information for that scope is popped from the top of the stack, which is the structure of the lexical environment. Note that when I say variables, I mean variables declared by let or const.

Next, when the console.log(a) line is executed in the scope block, we need to find the value of variable A in the lexical environment and variable environment by: Search down the stack of the lexical environment. If it finds something in a block in the lexical environment, it returns it directly to the JavaScript engine. If it doesn’t find something, it continues in the variable environment.

The variable lookup process is complete. You can refer to the following figure:

When the scoped block finishes execution, its internally defined variables are popped from the top of the lexical environment’s stack. The final execution context looks like this:

Block-level scope is implemented through the stack structure of the lexical environment, and variable promotion is implemented through the variable environment. By combining the two, the JavaScript engine supports both variable promotion and block-level scope.