定义 作用域链(Scope Chain)
当查找变量的时候,会先从当前上下文的变量对象中查找,如果没有找到,就会从父级(词法层面上的父级)执行上下文的变量对象中查找,一直找到全局上下文的变量对象,也就是全局对象。这样由多个执行上下文的变量对象构成的链表就叫做作用域链。
在词法作用域时讲过,函数在词法阶段(函数定义时)会创建该函数的词法作用域,保存在自身函数的[[Scope]]属性中,且不能被改变。
[[Scope]] 会保存所有父变量对象到其中,就是所有父变量对象的层级链。
Scope = AO + [[Scope]] Scope = [AO].concat([[Scope]]) // 一个执行上下文的完整作用域
function fn() {function foo() {// ...}}
函数在创建时的作用域为:
fn.[[Scope]] = [globalContext.VO]foo.[[Scope]] = [fnContext.AO,globalContext.VO]
在前面说到变量对象在创建阶段还会建立作用域,那么就来分析一下建立作用域的过程。
在创建变量对象 VO 时,会建立属性 Scope,其值是在词法阶段创建的词法作用域 [[Scope]]。
这时会复制 [[Scope]] 对象到属性 Scope 中,如下
VO = {arguments: {...},Scope: [[Scope]]}
又将活动对象 VO 添加到作用链的前端,Scope = [AO].concat([[Scope]]) 得到如下
VO = {arguments: {...},Scope: [AO, [[Scope]] ]}
最后再来看一个完整的示例
var a = 10function fn() {var b = 20return b}fn()
执行过程如下
- 创建函数
fn,同时创建词法作用域[[Scope]]
fn.[[Scope]] = {globalContext.VO}
- 执行函数
fn,创建该函数的执行上下文的变量对象,并初始化arguments和标识符
fnContext = {}fnContext.VO = {arguments: {length: 0},b: undefined}
- 建立该变量对象的作用域
fnContext = {VO: arguments: {length: 0},b: undefined,Scope: fn.[[scope]]}
- 将活动对象压入
fn作用域链顶端
fnContext = {VO: arguments: {length: 0},b: undefined,Scope: [ VO, [[scope]] ]}
- 开始执行
fn函数,修改AO的值
fnContext = {VO: arguments: {length: 0},b: 20,Scope: [ VO, [[scope]] ]}
- 执行完
fn函数,函数上下文fnContext从 ESC 推出。
ESC = [globalContext]
