felix-cao / Blog

A little progress a day makes you a big success!
31 stars 4 forks source link

JavaScript 作用域链(Scope Chain) #95

Open felix-cao opened 5 years ago

felix-cao commented 5 years ago

一、什么是链

链是数据结构术语,每个节点都有若干个指针指向其他节点或从其他节点指向该节点的指针,这些指针称为链。链也是组成超文本的基本单元,用来链接节点,是节点间的信息联系,它以某种形式将一个节点与其他节点连接起来。

链的结构一般可分为3个部分:链源、链宿及链的属性。链源是导致浏览过程中节点迁移的原因,可以是热标、媒体对象或节点等。链宿是链的目的所在,可以是节点,也可以是其他任何媒体内容。链的属性决定链的类型,是链的主要特性。

二、作用域链

作用域是一套规则,作用域链是这套规则的具体实现。

JavaScript 作用域 #59 一文中,我们知道:

作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限。JavaScript 的作用域是词法作用域

JavaScript变量对象(Variable Object简称VO) #57 一文中,我们知道:

JavaScript 代码的整个执行过程,分为两个阶段,编译阶段与执行阶段。作用域规则是在编译阶段确定的

我们还学习到执行上下文在创建阶段干了三件事:

浏览器执行一段 JavaScript 可执行代码(Executable code)时,会创建对应的执行上下文,执行上下文在创建阶段会创建变量对象,建立作用域链,以及确定this的指向。

作用域链,是由当前环境与上层环境的一系列变量对象组成,它保证了当前执行环境对符合访问权限的变量和函数的有序访问。

三、代码解析

var a = 20;
function foo() {
    var b = a + 10;
    function innerFoo() {
        var c = 10;
        return b + c;
    }

    return innerFoo();
}

foo();

在上面的例子中,全局,函数 foo,函数 innerFoo 的执行上下文先后创建。我们设定他们的变量对象分别为 VO(global),VO(foo), VO(innerFoo)。而 innerFoo 的作用域链,则同时包含了这三个变量对象,所以innerFoo的执行上下文可如下表示。

innerFooEC = {
    VO: {...},  // 变量对象
    scopeChain: [VO(innerFoo), VO(foo), VO(global)], // 作用域链
    this: {}
}

直接用一个数组来表示作用域链,数组的第一项scopeChain[0]为作用域链的最前端,而数组的最后一项,为作用域链的最末端,所有的最末端都为全局变量对象。

很多人会误解为当前作用域与上层作用域为包含关系,但其实并不是。以最前端为起点,最末端为终点的单方向通道我认为是更加贴切的形容。如图。

作用域链是由一系列变量对象组成,我们可以在这个单向通道中,查询变量对象中的标识符,这样就可以访问到上一层作用域中的变量了。