自我学习——javascript——闭包的本质
1.闭包的概念
闭包是指有权访问另一个函数作用域中的变量的函数(注意:闭包是个函数),创建闭包的常见方式是在一个函数内部创建另一个函数(ps:闭包不等于匿名函数,凡是在一个函数内部创建的函数并且内部引用到了外部函数的变量的函数都可以称为闭包,实际上,javascript内的所有函数都是闭包,因为都有作用域链)
2.闭包的现象
先上一个例子:
function createFunction(name){
var newFun=function(){
alert(name);
}
return newFun; //返回一个闭包
}
var resultFun=createFunction('xiaosi2');
resultFun();
在这个例子中,createFunction创建了一个函数newFun并且return回来,这个newFun就是一个闭包(注意newFun不是匿名函数)。而resultFun就是createFunction返回的那个函数,我们看到参数‘xiaosi2’是在createFunction这个函数中传入的,但是确是在resultFun这个函数执行的时候显示出来,说明了一个现象——resultFun可以访问createFunction的变量,即:使用闭包可以访问另一个函数的变量。但这同时也引起另一个问题——createFunction执行完了之后是需要被垃圾回收机制回收相应的内存空间的,并且‘xiaosi2’这个变量并没有在执行createFunction的时候变成全局变量,resultFun怎么能访问到?
ps:
1.newFun内用了name是createFunction的参数,如果newFun也有一个同名参数name,即
var newFun=function(name){}的话,newFun就不是闭包了,因为内部用到的参数不是外层作用域链的对象(即不是外层参数)
2.不是resultFun是闭包,而是newFun,resultFun不过是指向newFun罢了,即使newFun没有return出来,它也是闭包,因为可以访问到createFunction的变量,但是这样newFun会直接被垃圾回收掉,因为没有“用武之地”,显然这样的闭包对我们没什么用处
3.闭包的本质
我们刚才提到了,resultFun可以访问到createFunction内的变量是一件很奇怪的事情,实际上也没有很特别,不过是把resultFun内用到的createFunction内的变量保存下来了而已——保存用到的函数作用域链上用的对象(和原型链继承有些类似)
在函数的属性内有一个[[scope]]属性(这个属性在函数创建的时候就会存在,直至函数被销毁),保存的是scope chain列表,scope=AO(活动对象)+[[scope]],普通函数的作用域链只有2级,本身的参数变量(AO)和全局变量([[scope]]带来的,全局VA常驻内存),然而闭包函数因为引用到了其他函数(通常就是父级或者更上层)的变量,在作用域链内就会存储这个变量对象(注意,是用到什么才会存储什么,用不到的依旧会被销毁,在[[scope]]中被销毁的),这样就实现了newFun可以访问createFunction的变量,然而这并不是resultFun能访问createFunction的全部原因,因为后面又把resultFun指向newFun,这样就造成了对newFun的引用,从而垃圾回收不会回收newFun,所以我们就可以访问到没有被销毁的变量对象,也就是可以访问到‘xiaosi2’的原因
值得一提的是,函数是共享父级的[[scope]]的,所以一个函数对一个父级的变量更改的话会导致所有的函数访问到的,这也就是为什么没有引用到的变量会被销毁,因为在父级的函数内用不到就被销毁了,而子函数只是指向父函数AO而已
4.如何使用闭包
有趣的是,一般我们会使用狭义的“匿名函数闭包”的情况都是我们使用了另一个闭包的原因导致的。比如说for循环的执行事件:
function createFunction() { var result = new Array(); for (var i = 0; i < 10; i++) { result[i] = function() { console.dir(arguments.callee); return i; } } return result; } var newFun = createFunction(); for (var i = 0, max = newFun.length; i < max; i++) { console.log(newFun[i]()); //10 }