闭包的原理,优缺点,应用场景,常见面试题总结
1.概念
闭包:可以把闭包理解成一个函数,一个父函数里面嵌套的子函数(也就是函数中的函数),且该子函数必须使用了父函数的变量。
如:
function f1(){ var b=2; function f2(){ b++; console.log(b); } return f2; }; var f=f1(); f();
在上面代码中,f1()是父函数,而f2()是f1()的子函数,且f2中使用了父函数中的变量b。在这里,f2就是闭包。
闭包形成条件:
-
必须有一个内嵌函数
-
内嵌函数必须引用外部函数中的变量
-
外部函数的返回值必须是内嵌函数
2.生命周期
产生:在嵌套的子函数定义执行完成时就产生了
死亡:在嵌套的内部函数成为垃圾对象时
function f1(){ //此时就已经产生闭包(因为函数提升) var b=2; function fn(){ b++; console.log(b); } return fn; }; var f=f1(); f(); f=null//闭包消失,因为内部函数成为了垃圾对象(没有变量在引用它)
3.优缺点
优点:
(1)可以让子函数内的变量被访问
(2)可以让子函数的变量的值保留在内存里面不被清除
(3)方便调用上下文的局部变量
(4)加强封装性
缺点:
(1)由于闭包会让子函数的变量的值保留在内存里面,就容易造成大量内存被占用,内存消耗过大,可能会导致内存泄漏,影响网页性能。解决方法:及时清除不使用的局部变量,也就是赋值null。
(2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
4.常见面试题
(1)
function fun(n,o) { console.log(o); return { fun:function(m) { return fun(m,n); } }; } var a = fun(0); a.fun(1); a.fun(2); a.fun(3); //undefined,0,0,0 var b = fun(0).fun(1).fun(2).fun(3); //undefined,0,1,2 var c = fun(0).fun(1); c.fun(2); c.fun(3); //undefined,0,1,1
第一行:fun(0)即fun(0,o),o并未赋值->undefined, a其实是function()函数,也就是a是一个闭包,a=fun(0)返回的是fun(m,0),所以后面的fun(0); a.fun(1); a.fun(2); a.fun(3)都为0,因为闭包中的n没有变,都是同一个闭包a。
第二行:
同样,fun(0)即fun(0,o),o并未赋值->undefined, fun(0).fun(1)->fun(1,0)这时输出0,fun(0).fun(1).fun(2)->fun(0,1).fun(2)->fun(2,1)这时输出1,
fun(0).fun(1).fun(2).fun(3)->fun(2,1).fun(3)->fun(3,2),这时输出2
第三行:由于后面两个c.fun(2); c.fun(3)都是同一个闭包c在起作用,所以后面都是1
(2)
function f1(){ var n=999; nAdd=function(){n+=1} function f2(){ alert(n); } return f2; } var result=f1(); result(); // 999 nAdd(); result(); // 1000
result是f2(),所以调用result(),就是执行f2,由于全局变量n为999,所以这里就是999。
nAdd()这里执行了一遍函数,n变成了1000。
再执行一遍result(),此时n已经是1000了
(3)
function f1() { var n = 999; nAdd = function () { n += 1; }; function f2() { alert(n); } return f2; } var result1 = f1(); var result2 = f1(); result1(); // 999 result2(); //999 nAdd(); result1(); // 是999而不是1000,这是为何呢? result2(); //1000
var result1 = f1();
function f1(){ var n=999; //n在result1中的引用为temp1 var temp1 = n; nAdd=function(){ temp1 += 1; }; function f2(){ alert(temp1); } return f2; }
执行result2=f1()时:
function f1(){ var n=999; //n在result2中的引用为temp2 var temp2 = n; nAdd=function(){ temp2 += 1; }; function f2(){ alert(temp2); } return f2; }
由于result1和result2分别形成了闭包,分别对n进行了保存,所以顺着顺序执行nAdd();时,这里是对result2闭包中的n进行了修改,result1闭包把它自己的n保护起来了。
所以执行完nAdd(),result1()还是999,result2()变成1000。
(4)
function fn(){//创建父函数(爸爸) var arr = []; for(var i = 0;i < 5;i ++){//这里循环相当于创建了五个子函数(儿子) arr[i] = function(){ return i; } } return arr; } var list = fn();//这里只调用了一次父函数, for(var i = 0,len = list.length;i < len ; i ++){ console.log(list[i]()); } //5 5 5 5 5
参考博客:
https://blog.csdn.net/yingzizizizizizzz/article/details/72887161
https://blog.csdn.net/weixin_43586120/article/details/89456183
https://blog.csdn.net/yingzizizizizizzz/article/details/77726346
https://blog.csdn.net/u011043843/article/details/46640847?utm_source=app&app_version=4.5.7