JS|作用域链

147 阅读2分钟

在编程语言中都有作用域这个概念,在ES6之前,JS只有全局作用域和函数作用域两种,因为这种设计满足不了开发的需求,在ES6的时候添加了块级作用域{},并增加了两个关键字letconst用来替代var

var声明的变量会在执行上下文的变量环境中存储,而ES6新增的letconst定义的变量是存储在执行上下文的词法环境中的,而块级作用域中的变量又会在词法作用域中在单独存储一个区域。

而每个执行上下文的变量环境中,都包含了一个外部引用,用来指向外部的执行上下文,我们把这个外部引用称为outer。作用域链就是这样来的

我们在获取变量的值的时候会先去当前执行上下文中查询,没有命中的话就去outer指向的执行上下文中查询,以此类推,直至全局的执行上下文

那么关键就是这个outer指向的执行上下文是哪个:它是有词法解析阶段决定的,也就是说是所处的代码位置决定着outer指向的执行上下文是哪个,和调用的地方没有关系

看个例子:

function foo() {
    console.log(name)
}
function bar() {
    let name = '777'
    foo()
}
​
let name = 'zmheang'
bar()
// 按照常规应该是输出777,但是结果却是zmheang
// 这就是因为foo()和bar()的词法作用域都是全局执行上下文,所以在fo o()中没找到name变量,就会找outer指向的执行上下文,于是就是zmheang

词法作用域:就是指作用域是由代码中函数声明的位置来决定的,所以词法作用域是静态的作用域,通过它能够预测代码在执行过程中如何查找标识符

再看个例子:

function f1() {
    var name = 'zmheang'
    let test1 = 1
    if (1) {
        let name = '777'
        console.log(test)
    }
}
function f2() {
    var name = 'zm'
    let test = 2
    {
        let test = 3
        f1()
    }
}
var name = 'meng'
let age = 26
let test = 1
f2()
// 同理,f1()本身的执行上下文没有test,于是就去outer指向的执行上下文中去找,全局中的是test=1,于是输出1