Js 执行机制之作用域与块级作用域

251 阅读3分钟

What?

-- 变量与函数的可访问范围。在这个范围之外,对内部的变量和函数不可见

作用域的分类

  • 全局作用域

  • 函数作用域

        ES 通过变量环境实现函数级作用域

  • 块级作用域

    • 在 ES6 以前是不存在块级作用域的,为什么后来增加了块级作用域呢?
             -- 由于前面提到的 js 预编译过程中的变量提升特性,没有块级左右域会导致的下面两个问题,ES6 为了解决这个问题,块级作用域应运而生
      • 外层变量在不知不觉中被内部代码覆盖
      • 该销毁的变量未被销毁

    • 怎么实现的块级作用域?
             -- 使用前面 变量提升 中 JavaScript 执行流程中提到的 词法环境 中的栈结构来实现块级作用域
      • 使用 let 关键字,变量可在同一块级作用域中被修改
      • 使用 const 关键字定义变量,不可修改
      • 实现 let 和 const 关键词的过程

/* 块级作用域的实现过程 */
function foo3(){
    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);
}   
foo3()
/* 
    foo3 执行流程分析
    1、函数预编译并创建函数执行上下文
       (1)通过 var 关键字声明的变量则在此阶段存放在执行上下文中的变量环境里面
            a -> undefined
            c -> undefined
       (2)通过 let 关键字声明的变量则在此阶段存放在执行上下文中的词法环境中
            b -> undefined 注意:这里的 b 是块外面的 let 声明
    2、执行函数代码,执行到函数内的代码块时
       (1)变量环境变成:
            a -> 1;
            b -> 2;
       (2)按照在函数作用域中的规则一样,对块作用域中的let、const关键字声明的变量进行提升,并且在执行流进入到块中时,
            则将该块作用域提升的变量压入到词法环境中的栈结构中,当执行完成后弹出,此时词法环境为
            b -> undefined
            d -> undefined // 这里的 b 和 d 是块中使用 let 定义的变量

            b -> 2 //这是外层使用 let 定义的 b 变量
    3、继续往下执行,执行到打印变量 a 时,Js 引擎会率先在当前执行上下文的词法环境中沿着词法环境的栈顶往下寻找变量 a ,由于
       a 并不在词法环境中,那么就会进入变量环境中寻找,然后将变量环境中的 a = 1 返回给 Js 引擎,打印出 1
    4、接下来执行到打印变量 b 时,Js 引擎顺着 词法环境栈顶 --> 词法环境栈底 --> 变量环境的规则循环变量 b,发现在词法环境栈顶
       有变量 b 已经被赋值为 3 了,故打印出 b 为 3
    5、然后执行外层打印变量 b,此时执行上下文的词法环境中只剩下栈底的 b --> 2,故打印出 2 
    6、接着从变量环境中找到了 变量 c --> 4
    7、最后打印 d 时,由于此时词法环境栈中只有 b --> 2,变量环境中也未对变量 d 进行声明,故会引发 Js 抛出 ReferenceError 错误
    
    所以最后的执行结果为:
    1
    3
    2
    4
    ReferenceError 错误
*/