根据JavaScript高级程序设计,JavaScript核心以及汤姆大叔的深刻理解JavaScript系列总结。执行上下文、变量对象、作用域链等是从程序运行的角度解释的。
执行上下文
执行上下文(EC)顾名思义是指程序代码执行时候的环境,分为全局执行上下文、函数执行上下文以及eval执行上下文,每个函数都有执行上下文,当JavaScript程序执行不同的函数时会进入不同的执行上下文,一个执行上下文可能触发另外的执行上下文,比如一个函数调用另一个函数,故会产生上下文堆栈。
一个函数可能会创建无数的上下文,因为对函数的每次调用(即使这个函数递归的调用自己)都会生成一个具有新状态的上下文。
执行上下文栈
程序开始执行时会进入全局上下文(唯一的),全局上下文是执行上下文栈的第一个元素,当全局上下文执行过程中调用了一个函数,则该函数的执行上下文就会push到执行上下文栈中,当该函数又调用其他函数时,会进入新的执行上下文,并被push到栈中,当函数调用完成时会退出该执行上下文,进入外层的执行上下文继续执行,故栈底始终是全局上下文,栈顶始终是当前程序正在运行的执行上下文。
以程序为例:
var x=10;
function a() {
var y=20;
function b() {
var z=10;
console.log(x);
}
b()
}
a();
这个程序的执行上下文的变化是这样的:
栈底的元素始终是全局执行上下文,栈顶的元素始终是正在执行的函数的执行上下文。
变量对象
每一个执行上下文可以看成简单的对象,都有三个重要属性,变量对象,this,作用域链scope chain。在全局上下文中变量对象属性(variable Object,简称VO)包含全局变量和函数声明,不包含在内的有函数表达式以及没有使用var声明的变量(这种变量是,"全局"的声明方式,只是给Global添加了一个属性,并不在VO中)。
函数执行上下文中变量对象又叫活动对象(activitive Object,简称AO),活动对象包含arguments,传进去的形参,以及函数内部自定义的变量和函数声明。
var x=10;
function a() {
var w=30;
var y=20;
function b(p,q) {
var z=10;
console.log(p+q);
}
b(w,y)
}
a();
下图是全局变量对象与函数a,函数b的活动对象。
作用域链
作用域链是一个对象列表,是内部上下文所有变量对象(包括父变量对象)的列表。上下文代码中出现的标识符在这个列表中进行查找。解析标识符(变量名称、函数声明、形参等)时,会从作用域链中当前活动变量对象中查找,如果没有找到则会向作用域链的上一层查找,直到找到为止。
这里作用域链用Scope表示,它包括两部分,一是当前执行上下文的变量/活动对象用VO/AO表示,二是函数内部有一个特殊的[[Scope]]属性,保存父级的作用域链(包含父级变量对象+[[Scope]]),当函数创建的时候,函数内部特殊的[[Scope]]属性就存储了父级的作用域链,并且是静态的,即Scope=VO/AO+[[Scope]]。
可以看到当前[[Scope]]保存的是父级的整个作用域,包含父级的活动对象以及其函数的[[Scope]]属性,也就是说当前的执行上下文中包含所有的活动对象和全局变量对象。
作用域链与原型链的二维查找
当这些函数或者对象拥有原型时,原型链和作用域链的查找顺序是:在当前执行上下文中会先查找该执行上下文的活动变量,然后查找其原型链__proto__(如果有原型链),如果原型链上没有,再查找[[Scope]]中活动对象父作用域链,深入每一个作用域链的原型链。即活动对象→原型链→[[Scope]]中父级活动对象→[[Scope]]中父级原型链→[[Scope]]中上一级的[[Scope]]中父级活动对象……
闭包
在函数创建时[[Scope]]属性就存储了父级的作用域链,这样返回这个函数时就可以访问父函数的作用域,这就是闭包。理论上来说,由于所有的函数都有[[Scope]]属性,所有的函数都能看成闭包(由于全局作用域的存在)。通过new Function()函数创建的函数的[[Scope]]属性保存的总是全局对象的作用域链。
多个函数可能拥有相同的父作用域(这是很常见的情况,比如for循环传递i的值到内部函数的时候)。在这种情况下,它们的父作用域链是相同的,[[Scope]]属性中存储的变量是在拥有相同父作用域链的所有函数之间共享的。即在这个函数里改变了变量的值,另一个函数中也改变了。立即执行函数可以改变相同父作用域的共享,因为立即执行函数相当于新建了一个作用域链,那么就不是相同的父作用域链了。而且在所有函数中参数的传递都是值传递。
参考:
http://weizhifeng.net/javascript-the-core.html#execution-context-stack
http://www.cnblogs.com/tomxu/archive/2011/12/15/2288411.html