javascript中有scope(作用域),scope chain(作用域链),execute context(执行上下文),active object (活动对象),dynamic scope(动态作用域),closure(闭包)这些概念,要理解这些概念,我们从静态和动态两个方面去分析一下。
首先我们写一个简单的function来做一个例子:
复制代码 代码如下:
function add(num1, num2){
var sum = num1 + num2;
return sum;
}
我们定义了一个具有两个形参的add函数。
静态方面:
当创建add函数的时候,javascript引擎会创建add函数的scope chain,这个作用域链指向了global context(全局上下文)。如果用图形形象化的表述如下图所示:
从上图可以看出,当add函数创建的时候,作用域链就已经创建了,因此可以得出一个结论,函数的作用域链是创建函数的时候就已经创建了,而不是动态运行期。下面就来看看动态运行期的时候会发生什么事情。
动态方面:
当执行add函数的时候,javascript会创建一个execute context(执行上下文),执行上下文中就包含了add函数运行期所需要的所有信息。execute context也有自己的scope chain,当函数运行的时候,javascript引擎会首先从用add函数的作用域链来初始化执行上下文的作用域链,然后javascript引擎又会创建一个active object,这个对象里面包含了函数运行期的所有局部变量,参数以及this等变量。
如果形象的描述add函数动态运行期会发生什么,可以用如下图来描述:
从上图可以看出,执行上下文是一个动态的概念,它是当函数运行的时候创建的,同时active object对象也是一个动态的概念,它是被执行上下文的作用域链引用的。因此可以得出一个结论:执行上下文和活动对象都是动态概念,并且执行上下文的作用域链是由函数作用域链初始化的。
上面说了函数作用域和执行上下文作用域,下面接着说一下动态作用域的问题,当在javascript通过with语句,try-catch的catch子句,以及eval方法的时候,javascript引擎就会动态的改变执行上下文的作用域。下面还是通过一个例子来看看:
复制代码 代码如下:
function initui(){
with (document){ //avoid!
var bd = body,
links = getelementsbytagname(a),
i= 0,
len = links.length;
while(i update(links[i++]);
}
getelementbyid(go-btn).onclick = function(){
start();
};
bd.classname = active;
}
当执行上面的initui函数的时候,javascript会动态的创建一个with语句对应的作用域放到执行上下文作用域链的最前端,通过下图可以形象的描述上述过程,下图红色标注的区域就显示了with语句产生的作用域。
最后,我们来看看javascript最神秘的closure(闭包),闭包在javascript其实就是一个函数,闭包是在函数运行期被创建的,下面还是以一个实例来看看:
复制代码 代码如下:
function assignevents(){
var id = xdi9592;
document.getelementbyid(save-btn).onclick = function(event){
savedocument(id);
};
}
当上面的assignevents函数被执行的时候,会创建一个闭包,而这个闭包会引用assignevents作用域中的id变量,如果按照传统的编程语言的方式,id是存储在堆栈上的一个变量,当函数执行完了以后id就消失,那么怎么可能再次引用呢?显然这里javascript采用了另外的方式。下面就来看看javascript是如何来实现闭包的。当执行assignevents函数的时候,javascript引擎会创建assignevents函数执行上下文的作用域链,这个作用域链包含了assignevents执行时的活动对象,而同时javascript引擎也会创建一个闭包,而闭包的作用域链也会引用assignevent执行时候的活动对象,这样当assignevents执行完的时候,虽然它本身执行上下文的作用域链不再引用活动对象了,但是闭包还是引用着assignevents运行期对应的活动对象,这就解释了javascipt内部的闭包机制。可以用下图形象的表述上面assignevents函数运行期的情形:
从上面可以看出,当assignevents函数执行完毕以后,document.getelementbyid(save-btn).onclick引用了闭包,这样当用户点击save-btn的时候,就会触发闭包的执行,那么下面就来看看闭包执行时的情形。前面也说了javascript中闭包其实就是函数,因此闭包执行和函数执行时的情形是一致的,通过下图来形象的描述上述onclick事件所关联的闭包。
从上图可以看出javascript引擎首先创建了闭包的执行上下文,然后用闭包作用域链来初始化闭包的执行上下文作用域链,最后再将闭包执行时对应的活动对象放入到作用域的最前端,这也进一步验证了闭包就是函数的论断。