1.闭包的概念
闭包:是指有权访问另外一个函数作用域中的变量的函数。
缺点:占用内存。(原因:由于闭包函数可以访问外层函数中的变量,所以外层函数在执行结束后并不会被销毁,(注意:外层函数执行结束后执行环境和对应的作用域链就会被销毁),而是被闭包函数的作用域链所引用,直到闭包函数被销毁后,外层函数的作用域活动对象才会被销毁,这就是闭包占用内存的原因)
这时候就引入了一个申明变量的关键字let,我们把var关键替换为let,就能实现了输出0,1,2.........,至于为什么,我在这里稍微提一下,这就需要了解var关键字和let关键字申明变量的区别了,关于let关键字可以查看阮一峰的ECMAScript6入门中的第二章:let和const命令,当使用在for循环中的时候, let 只是在 for 循环中,而 var 却是在循环体外都是可见的,这就是为什么在关键字不同的时候输出的结果不一样的原因,这里就扯远了。
1创建闭包
创建闭包的常见方式就是在一个函数内部创建另外一个函数。
function f1(){ var n=999; function f2(){ alert(n); } return f2; } var result=f1(); resule(); // 999
在上述代码中,函数f2就被包括在函数f1的内部,这时f1内部的所有局部变量,对f2都是可见的,但是f2内部的局部变量,对f1就是不可见的,既然f2可以读取f1中的局部变量,那么只要把f2作为返回值,这样就可以在f1外部读取它的内部变量了
创建闭包 闭包的用途:在javascript中没有块级作用域,一般为了给某个函数声明一些只有该函数才能使用的局部变量时,我们就会用到闭包。
闭包的优缺点:
优点:可以很大程度上减少全局作用域中的变量,净化全局作用域。
如何证明外层的函数执行结束后没有被销毁:
function f1(){ var n=999; nAdd=function(){n+=1} // 全局变量,匿名函数,也是一个闭包 function f2(){ alert(n); } return f2; } var result=f1(); resule(); // 999 nAdd(); result(); //1000
在这段代码中,result实际上就是f2的闭包函数,它一共运行了两次,第一次的值是999,第二次的值是1000,这就证明了f1中的局部变量n一直保存在内存中,并没有在f1调用后被自动清除。
为什么会这样呢?
原因就在于f1是f2的父函数,而f2被赋给了一个全局变量,这导致f2始终在内存中,而f2的存在依赖于f1,因此f1也始终在内存中,不会在调用结束后,被垃圾回收机制(garbage collection)回收。
3.使用闭包的注意点
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。解决方法是,在退出函数之前,将不使用的局部变量全部删除。
- 闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
const greeter = []; for(var i=0;i<10;i++){ greeter.push(function(){ return console.log(i) }) }; greeter[0]();//10 greeter[1]();//10这里初学者会认为,输出结果为0,1....。你知道为什么输出的不是0,1,2......而是10吗?应该要怎样修改才能让程序输出的为0,1,2,.....
这时候就引入了一个申明变量的关键字let,我们把var关键替换为let,就能实现了输出0,1,2.........,至于为什么,我在这里稍微提一下,这就需要了解var关键字和let关键字申明变量的区别了,关于let关键字可以查看阮一峰的ECMAScript6入门中的第二章:let和const命令,当使用在for循环中的时候, let 只是在 for 循环中,而 var 却是在循环体外都是可见的,这就是为什么在关键字不同的时候输出的结果不一样的原因,这里就扯远了。