【ES6】深入了解块级作用域

140 阅读2分钟

写在开头

我们可以知道在ES6面世之前是没有块级作用域这一说法的,那么块级作用域是如何实现的呢?块级作用域又是如何和变量提升共存的呢?

执行上下文

执行上下文相信大家都不陌生。当 JavaScript 代码执行的时候,会进入不同的执行上下文。不同的执行上下文就是不同代码块的执行环境。

调用栈

聊到执行上下文的话就一定离不开调用栈。调用栈是用来管理函数调用关系的一种数据结构,用来装js代码中的各种执行上下文。函数调用的特点是:越早被调用的函数,越晚返回。 栈完美的符合了这个条件。

let a = '111';
function foo() {  
  console.log('222');  
  baz();  
  console.log('333');  
}
function baz() {  
  console.log('444');  
}
foo();  
console.log('555');

//222 444 333 555

image.png 这段代码的调用栈大概就是这个样子。代码的执行都是先有全局执行上下文,然后调用foo时将foo压入栈中,调用baz时将baz压入栈中,待执行结束后依次出栈。

块级作用域是如何实现的呢?

先来看一段代码。

function foo() {
    var a = 1
    let b = 2
    {
      let b = 3
      var c = 4
      let d = 5 
      console.log(a); //1
      console.log(b); //3
    }
    console.log(b); //2
    console.log(c); //4
    console.log(d); //undefined
  }
  foo()

首先我们可以知道的是调用栈在foo执行的时候是这样的:

image.png

那么foo的执行上下文是怎么样的呢?这个时候我们又要注意 varlet 了。其实 var 声明的变量是存放在变量环境的,let声明的变量是存放在词法环境的,而且内部的let定义的变量会另外开辟作用域并存放变量置栈顶。此时我们可以画出foo执行上下文的图示。

image.png

根据打印结果我们可以知道, 变量的查找方式是从词法环境的作用域栈顶开始向下查找,找到了,就返回值,找不到,就继续去变量环境中查找。

总结

块级作用域是通过词法环境的栈结构来实现的,变量提升是通过变量环境实现的,两者的实现是不会互相影响的,于是就同时实现了块级作用域与变量提升。