作用域链与变量对象
执行上下文栈:
在执行js时,一层层的执行上下文叠成执行上下文栈,全局上下文是栈底,当一个函数被调用是,会将相应的执行上下文压入栈,执行完便出栈。
执行上下文
Execution Context,即EC,形象的说:12345EC={ 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生成相应属性,如有同名的,则替换为对应的函数对象(函数声明提前),注意:只针对函数声明语句,非声明语句不处理
特别的:12var e = function f(){};f;//Error, f is not defined. 因为上面代码不是函数声明语句,初始化时只被当做变量e的声明,而函数f被忽略,但到代码执行阶段,会将函数对象赋给e,通过e访问函数,依然不定义f
类似的:12(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按栈的顺序串连起来的访问链