闭包的概念
我认为首先应该先了解作用域以及作用域链。
函数在声明的时候,该函数的属性[[scope]]就已经包含了一个叫做scope chain的指针列表。它指向的是这个函数的当前的环境(如果不是全局)一直到全局的环境对象的一个列表。
当函数调用,但是还没有执行的时候,test的执行环境通过复制这个函数的[[scope]]属性中的对象,然后加上这个函数的活动对象作为这个scope chain链的顶端,这样就构成了一个执行环境的作用域链。
在这个链上,访问的顺序也只能是由最内部的函数去访问最外层的函数中的变量和对象。因为在运行完这个函数之后,它的活动对象和执行环境就被销毁了。
function test() {
var a = 5;
return a;
}
test();
alert(a);//报错
而使用闭包,就能让外部访问到内部的方法和变量。
function test(){
var a = 5;
return function add(){
a +=3;
return a;
}
}
var link = test();
alert(link);//function add(){}..上面的add函数
alert(link());//8
link在这里是一个指向函数add的引用,虽然它可以返回函数的内容,但是如果link改变了,不会影响add。
一般情况下,我们是不能去在全局下直接调用这个add函数,也就是test的方法。而这个add方法,它还访问了test函数中的a变量。于是在全局下,我们能借助add函数的返回,弹出了a的结果。
这个函数add就是闭包。
我个人的对它的名字理解是它在本来封闭的环境,从这个环境的局部把“东西”带了出来。
实际它的原理
上面的这个add函数,它在定义的时候携带了test函数的活动对象,它的执行环境的作用域链上分别有全局的变量对象,test的活动对象,然后就是这个闭包add的变量对象。当test函数返回的时候,它的执行环境就默认被销毁了。但是它的活动对象还仍然保留在返回的add函数的作用域链上,add的作用域链还在引用test的活动对象,所以,test的活动对象并没有被销毁。直到add的执行环境销毁时,test的活动对象才会在内存中消失。