这篇博客已经酝酿很久了,但是仍然感觉自己对这方面的内容还没达理解融汇的地步,加油啊!这篇博客是对我另一篇博客的更详尽的说明补充,下文有提到。
JS中的作用域ES6之前只有两种,全局作用域(Global Scope),局部作用域(Local Scope又称函数作用域),作用域顾名思义就是变量能够起作用的区域,全局作用域定义的变量在全局范围内都有用。在函数2作用域内,局部作用域定义的变量的优先级大于前者,并且只在这个函数范围内有用。至于作用域链是由作用域构建的。
执行期上下文(execution context)是函数执行前被创建的,里面有三个属性,
1,变量对象VO,variable object
2,作用域链,Scope Chain
3,this,
我们以函数执行为线索来探究他们到底有什么联系,首先看代码
var name = 'Joe';
function call(){
var name = 'Joi';
console.log(name);//'Joi'
}
call();
为什么不是 'Joe' 呢?
一,首先 call 函数被创建,保存作用域链到函数内部属性[[ Scope ]],这一步其实是继承全局的作用域,
call.[[scope]] = [
globalContext.VO//全局的执行上下文
]
二,开始着手执行函数,创建执行上下文execution object,不是真的执行,毕竟作用域中啥也没。
callExecutionContext{
AO
scope chain
this
}
三,函数并不立即执行,需要一定准备工作,初始化执行上下文 execution object
1,,复制函数属性[[ scope ]]来创建作用域链,
callExecutionContext{
AO
scope chain:[
call.[[scope]
];
this
}
2,用arguments来创建活动对象,并且初始化,具体过程请参考我的博客《JS预编译》,大概过程就是加入形参,函数声明,变量声明等属性,
callExecutionContext{
AO:{
name:undefined;
……
}
scope chain:[
call.[[scope]
];
this
}
3,将上一步创建的活动对象压入作用域链顶端
callExecutionContext{
AO:{
name:undefined;
……
}
scope chain:[
AO
call.[[scope]
];
this
}
四,准备工作完成开始执行函数,随着函数执行,作用域中的变量也随之被赋值
callExecutionContext{
AO:{
name:Joi;
……
}
scope chain:[
AO
call.[[scope]
];
this
}
五,函数执行完毕,函数的执行上下文销毁,(是否完全销毁看情况)
可以看出,其实作用域链和作用域是难解难分的,它可以理解为执行上下文的集合,当例子中要访问name属性时,去作用域中寻找相应的属性,如果没有就往下,在globalExecutionContext中寻找。以此类推。而整个过程我们可以叫做预编译,他为函数的执行做了准备工作。
AO(activation object)和VO(variable object)其实是一个东西,只不过是不同时期的叫法而已,当上一级函数的作用域中的AO随着下一级函数的AO压入作用域时,才会变成VO,才可以访问各种属性,换句话说,只有下一级函数继承上一级函数作用域完成时,上一级函数的活动对象才会变成VO。
参考资料,红宝书,冴羽(超级厉害)的《深入JS系列》,你不知道的JS,等。