函数表达式-闭包
闭包难理解,书上写得有些难理解,觉得有些人的博客写的很好就借鉴了一下
闭包
闭包是指有权访问另一个函数作用域中的变量的函数。创建闭包的常见方式,就是在一个函数内部创建另一个函数
闭包是很多语言都具备的特性,在js中,闭包主要涉及到js的几个其他的特性:作用域链,垃圾(内存)回收机制,函数嵌套,等等.
在理解闭包以前.最好能先理解一下作用域链的含义,简单来说,作用域链就是函数在定义的时候创建的,用于寻找使用到的变量的值的一个索引,而他内部的规则是,把函数自身的本地变量放在最前面,把自身的父级函数中的变量放在其次,把再高一级函数中的变量放在更后面,以此类推直至全局对象为止.当函数中需要查询一个变量的值的时候,js解释器会去作用域链去查找,从最前面的本地变量中先找,如果没有找到对应的变量,则到下一级的链上找,一旦找到了变量,则不再继续.如果找到最后也没找到需要的变量,则解释器返回undefined.
了解了作用域链,我们再来看看js的内存回收机制,一般来说,一个函数在执行开始的时候,会给其中定义的变量划分内存空间保存,以备后面的语句所用,等到函数执行完毕返回了,这些变量就被认为是无用的了.对应的内存空间也就被回收了.下次再执行此函数的时候,所有的变量又回到最初的状态,重新赋值使用.但是如果这个函数内部又嵌套了另一个函数,而这个函数是有可能在外部被调用到的.并且这个内部函数又使用了外部函数的某些变量的话.这种内存回收机制就会出现问题.如果在外部函数返回后,又直接调用了内部函数,那么内部函数就无法读取到他所需要的外部函数中变量的值了.所以js解释器在遇到函数定义的时候,会自动把函数和他可能使用的变量(包括本地变量和父级和祖先级函数的变量(自由变量))一起保存起来.也就是构建一个闭包,这些变量将不会被内存回收器所回收,只有当内部的函数不可能被调用以后(例如被删除了,或者没有了指针),才会销毁这个闭包,而没有任何一个闭包引用的变量才会被下一次内存回收启动时所回收.
闭包与变量
1 var result = new Array(); 2 function createFunctions(){ 3 4 for(var i = 0; i < 10; i++){ 5 result[i] = function(){ 6 console.log(i); 7 }; 8 } 9 10 return result; 11 } 12 createFunctions(); 13 result[0](); //10 14 result[5](); //10 15 result[8](); //10
表面上看,似乎每个函数都应该返回自己的索引值,但是实际上,每个函数都会返回10,因为每个函数的作用域链中都保存着createFunction()函数的活动对象,所以他们都引用的是同一个变量i(闭包中所记录的自由变量,只是对这个变量的一个引用,而非变量的值,当这个变量被改变了,闭包里获取到的变量值,也会被改变)
解决方法就是立即调用匿名函数
1 var result = new Array(); 2 function createFunctions(){ 3 4 for(var i = 0; i < 10; i++){ 5 result[i] = ( function(num){ 6 return function(){ 7 console.log(num); 8 }; 9 10 })(i); 11 } 12 13 return result; 14 } 15 createFunctions(); 16 result[0](); //0 17 result[5](); //5 18 result[8](); //8
关于this对象
this对象是在运行时基于函数的执行环境绑定的:在全局函数中,this等于window,而当函数被作为某个对象的方法调用时,this等于那个对象,不过,匿名函数的执行环境具有全局性,因此其this对象通常指向window
1 var name = "The Window"; 2 3 var object = { 4 name : "My Object", 5 getNameFunction : function(){ 6 return function(){ 7 return this.name; 8 }; 9 } 10 }; 11 12 console.log(object.getNameFunction()()); //The Window
如果我们把外部作用域中的this对象保存在一个闭包能够访问到的变量里,就可以让闭包访问该对象了
1 var name = "The Window"; 2 3 var object = { 4 name : "My Object", 5 getNameFunction : function(){ 6 var that = this; 7 return function(){ 8 return that.name; 9 }; 10 } 11 }; 12 13 console.log(object.getNameFunction()()); //My Object
注意:
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值
参考资料
1、墓中无人 :js中的闭包之我理解 - http://www.cnblogs.com/mzwr1982/archive/2012/05/20/2509295.html#3433599
2、阮一峰的网络日志 :学习Javascript闭包(Closure)- http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html