作用域链与变量对象

参考博文

执行上下文栈:

  在执行js时,一层层的执行上下文叠成执行上下文栈,全局上下文是栈底,当一个函数被调用是,会将相应的执行上下文压入栈,执行完便出栈。

执行上下文

Execution Context,即EC,形象的说:

1
2
3
4
5
EC={
VO:{ },//变量对象,Variable Object,保存有当前作用域的相关的变量和声明
this:{ },
Scope://当前作用域的作用域链,有自身以及父辈上下文的AO或VO连成,AO即Active Object,活动对象
}

VO与AO

作用域中的形参、声明的函数名和变量以属性的形式存在于AO、VO

VO:

  • 在全局上下文中,VO==全局对象,全局变量可以以全局对象的属性的形式访问(在某种程度佐证)
  • 在函数上下文中,VO不能直接访问,要通过AO访问,基本上AO==VO

AO:

  Active Object,活动对象,函数的执行上下文才有,AO特有arguments属性,对应函数中的arguments

处理上下文代码的过程:

1.进入执行上下文:

初始化VO

  • 处理函数形参:根据形参名,在VO生成相应的属性,并赋予实参值,没传实参的赋undefined
  • 处理函数声明:在VO生成相应属性,如有同名的,则替换为对应的函数对象(函数声明提前),注意:只针对函数声明语句,非声明语句不处理

  特别的:

1
2
var e = function f(){};
f;//Error, f is not defined. 因为上面代码不是函数声明语句,初始化时只被当做变量e的声明,而函数f被忽略,但到代码执行阶段,会将函数对象赋给e,通过e访问函数,依然不定义f

  类似的:

1
2
(function x(){});
x;//Error, x is not defiend.

  • 处理变量声明:在VO生成相应属性,赋值为undefined(变量声明提前),若VO有同名的,则忽略变量声明,不改变对应的同名VO属性值

初始化AO

  • 在AO中生成arguments属性
  • 跟据VO内的属性,在AO内生成对应的,并赋值(不知道是复制还是直接赋引用)

2.执行代码

  根据每行代码的执行结果,修改AO或VO里的属性(即作用域里的变量)

Scope作用域链与[[scope]]:

  • [[scope]]是函数的内部属性,在函数定义或创建时,创建函数的上下文和该上下文的所有父上下文的变量对象组成链,并存在函数的[[scope]]中。
  • Scope是执行上下文的一个属性,在函数调用激活时,以当前函数执行上下文的AO为头,拼接上[[scope]],即形成作用链,存在Scope中

  所以,作用域链是由执行上下文栈中的当前和所有父上下文的AO或VO按栈的顺序串连起来的访问链