js中的Scope Chain

1,213 阅读2分钟

一介绍

我们都知道变量对象会保存执行上下文中的一些数据,例如function declarations、formal parameters、variable、arguments,这些数据都会成为变量对象的属性。

当一进入执行上下文的时候,变量对象会进行初始化,在代码执行的阶段变量对象中的一些数据也会进行更新。

同样与执行上下文密切相关还有作用域链,本篇文章将会介绍执行上下文中的作用域链。

二定义

作用域链与执行上下文密切相关,一系列变量对象组成的链常用于在标识符解析时查找变量。

让我们先来举一个简单的例子:

var x = 10;
function Foo() {
var y = 20;
function bar() {
    console.log(x + y);
}
return bar;
}
Foo()();      //30

我们都知道没进入一个执行上下文,就会有一个与该上下文密切相关的变量对象。对于全局上下文(global context)来说,其变量对象(variable object)就是全局对象(global object)。对于函数上下文来说就是活动对象(activation object),也称为变量对象。

对于内部上下文来说,其作用域链(Scope Chain)是由所有父级的变量对象和自身的变量对象组成的。这条链常用于变量的查找。在上面这个例子中函数bar上下文的作用域链包括bar的活动对象、Foo的变量对象、全局变量对象。

当函数调用时,该函数上下文中的作用域链就会被创建。并且是由该函数上下文中的活动对象和函数的内部属性[[scope]]所组成的。 就像下面这样:

activeExecutionContext = {
    VO: {...}, // or AO
    this: thisValue,
    Scope: [ // Scope chain
      // list of all variable objects
      // for identifiers lookup
    ] 
};

其作用域就是下面这样:

Scope = AO + [[scope]]

常常将作用域链放在一个栈中,例如用js中的数组来模拟栈可以表示成下面这个样子:

var Scope = [vo1,vo2,vo3,...]   //scope chain

当然你也可以用对象来表示,通过引用父级作用域表示成分层对象链,用__parent__属性来指向外部上下文中的变量对象。 例如:

var VO1 = {__parent__: VO2, ... other data}; -->
var VO2 = {__parent__: VO3, ... other data}; -->
// etc.

以上这种Scope = AO + [[scope]]的标识符解析过程,与函数的生命周期有关。将会在下面继续讨论