Skip to content

作用域链 ​

本节目标:理解“作用域链”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 变量对象的作用就是保证对变量和函数的正确访问; 而作用域链的用途是保证对变量和函数的有序访问。

作用域链本质上是一个指向变量对象的指针列表(可使用数组表示);
它只引用但不实际包含变量对象。

最前:作用域链的前端始终都是当前执行上下文的变量对象,如果这个执行上下文属于函数执行上下文,则用活动对象作为变量对象。

最后:全局执行上下文的变量对象(也就是全局对象)始终是作用域链的最后一个对象。

**创建过程**
==作用域链是一个动态的创建过程,通过例子理解一下:==
var number = 1;
compare(5, 10);
function compare(value1, value2) {
    inner();
    if (value1 < value2) {
        return -1;
    } else if (value1 > value2) {
        return 1;
    } else {
        return 0;
    }
    function inner() {
        var a = 1;
    }
}
**全局阶段**

创建全局上下文:首先在执行全局代码前,先创建全局上下文。

创建全局变量对象:创建全局上下文的第一步是创建全局变量对象

创建作用域链:将全局变量对象放入作用域链的顶端,执行上下文中的[[Scope]]属性指向作用域链。如图所示:

此时全局上下文中的[[Scope]]属性可以这样表示
globalContext.[[Scope]] = [
    globalContext.VO
];

为变量对象所有函数添加[[Scope]]属性:在创建完作用域链后,JavaScript 引擎还执行了最关键的一步,它会为变量对象中的所有函数添加一个[[Scope]]属性,而这个属性的值就是我们刚才介绍的全局上下文中的[[Scope]]属性值。如下,

compare.[[Scope]] = [
    globalContext.VO
]

==全局上下文创建后,开始执行代码:根据代码动态的修改变量对象中的属性。==

**函数阶段**
如果代码中存在函数的调用:

创建函数上下文:在执行compare(5,10)之前,我们会为函数创建对应的执行上下文。

复制[[Scope]]属性:函数上下文首先会复制函数的[[Scope]]属性用来创建作用域链。

创建活动对象:然后用arguments,函数参数,函数内变量声明和函数声明创建活动对象,再将活动对象压入作用域链顶端。

comapre执行上下文复制[[Scope]]属性创建作用域链后可以这样表示
compareContext = {
    Scope: compare.[[Scope]],
}
压入活动对象后作用域链如下
checkScopeContext = {
    AO: {
        ...
    },
    Scope: [AO, [[Scope]]]
}

为活动对象所有函数添加[[Scope]]属性:在创建好作用域链后,JavaScript 引擎同样会为变量对象中的所有函数添加一个[[Scope]]属性,而这个属性的值就是当前函数上下文中的[[Scope]]属性值。如所示

inner.[[Scope]] = [
    checkScopeContext.AO,
    globalContext.VO
];

在代码执行阶段会对函数上下文中活动对象属性进行动态修改,因为作用域链是对变量对象的引用,因此我们可以实时地获取变量对象的最新状态,保证对作用域链查询时能够保证变量的准确性。

如果函数内的代码中还有函数调用,将重复一次上面的过程。

函数代码执行完毕后,出栈,销毁变量对象中数据,转交执行权给外部函数执行上下文。

本站总访问 — 次