1.理解闭包
1.1 如何产生闭包?
当一个嵌套的内部(子)函数引用了嵌套的外部(父)函数的变量(函数)时,就产生了闭包
1.2 闭包到底是什么?
i:闭包是嵌套的内部函数
ii:包含被引用变量(函数)的对象
iii:闭包存在于嵌套的内部函数中
1.3 产生闭包的条件:
i:嵌套的函数
ii:内部函数引用了外部函数的数据(变量/函数)
function foo(){ var a = 1; function bar(){ //内部函数bar引用了外部函数foo的a变量,此时产生闭包 console.log(a); } bar() } foo();
2.常见的闭包
2.1 将函数作为另一个函数的返回值
function fn1(){ var a = 1; function fn2(){ a++; console.log(a); } return fn2 } var fn = fn1();//产生闭包,变量a = 1 fn() //2 fn() //3
2.2 将函数作为实参传递给另一个函数调用
function delay(msg, time){ setTimeout(function(){ alert(msg) }, time) } delay('Hello Closure', 1000)
3.闭包的作用
3.1 使用函数内部的变量在函数执行完后,仍然存活在内存中(延长了局部变量的生命周期)
3.2 让函数外部可以操作(读/写)到函数内部的数据(变量/函数)
4.闭包的生命周期
4.1 产生:在嵌套的内部函数定义执行完成时就产生了(不是在调用)
4.2 死亡:在嵌套的内部函数成为垃圾对象时
function foo(){ //此时闭包已经产生(函数提升,内部函数已经创建) var a = 1; function bar(){ console.log(a); } return bar; } var f = foo(); f() f = null //闭包死亡(包含闭包的函数成为垃圾对象)
5.闭包的使用:自定义JS模块
(function (window){ //私有数据 var msg = "Hello closure"; function doSomething() { console.log("doSomething() "+msg.toUpperCase()); } function doOtherthing() { console.log("doOtherthing() "+msg.toLowerCase()); } //向外暴露对象 window.module = { doSomething:doSomething, doOtherthing:doOtherthing } })(window)
module.doSomething(); //doSomething() HELLO CLOSURE module.doOtherthing(); //doOtherthing() hello closure
6.闭包的缺点及解决
6.1 缺点:
i:函数执行完后,函数内的局部变量没有释放,占用内存时间会变长
ii:容易造成内存泄漏
6.2 解决:
i:能不用闭包就不用
ii:及时释放
function fn1() { var ary = new Array(100000); function fn2() { console.log(ary) } return fn2; } var fn = fn1() fn() fn = null; //让内部函数成为垃圾对象-->回收闭包
补充知识点:
1.内存溢出
i:一种程序运行出现的错误
ii:当程序运行需要的内存超过了剩余内存时,就抛出内存溢出的错误
2.内存泄漏
i:占用的内存没有及时释放
ii:内存泄漏积累多了,容易导致内存溢出
iii:常见的内存泄漏:意外的全局变量,没有及时清理的定时器或者回调函数,闭包
//内存溢出 var obj = {}; for(var i = 0; i < 10000; i++){ obj[i] = new Array(1000000) } //浏览器报错:显示此网页时内存不足
//内存泄漏 //意外的全局变量 function fn() { ary = new Array(100000) console.log(ary) } fn() //没有及时清理的定时器或者回调函数 var intervalId = setInterval(function() { console.log("hello closure") }, 1000) clearInterval(intervalId) //清除定时器 //闭包 function fn1() { var ary = new Array(100000); function fn2() { console.log(ary) } return fn2; } var fn = fn1() fn()
面试题1
//code1 var name = "outside" var obj = { name:"inside", getNameFunc:function(){ return function(){ return this.name } } } alert(obj.getNameFunc()()) //? 是否有闭包? //code2 var name = "outside" var obj = { name:"inside", getNameFunc:function(){ var that = this; return function(){ return that.name } } } alert(obj.getNameFunc()()) //? 是否有闭包
面试题2
function foo(v1, v2){ console.log(v2); return { foo:function(v3){ return foo(v3, v1); } } } var a = foo(0); a.foo(1); a.foo(2); a.foo(3) //? var b = foo(0).foo(1).foo(2).foo(3)//? var c = foo(0).foo(1); c.foo(2); c.foo(3)//?
PS:面试题答案参考评论区
热门评论
1.var a = foo(0); a.foo(1); a.foo(2); a.foo(3)
var a = foo(0) --> 此时 a 等同于
{
foo:function(v3){
return foo(v3, v1);
}
}
输出undefined
产生闭包,此时闭包里的变量v1等于0
a.foo(1) --> 调用的时foo:function(v3) --> return foo(v3, 0)
输出 0
由于执行了foo(外部),产生新闭包,但闭包随着foo()调用结束后消失
换个角度理解:
如果 var x = a.foo(1) 则此时产生闭包,闭包里面的变量v3等于1
但是由于 a.foo(1) 没有变量接受,所以闭包随之消失
同理,a.foo(2), a.foo(3) 始终调用的a里面产生的闭包,变量v1 = 0;
所以:a.foo(2), a.foo(3) // 0, 0
所以最终输出:
undefined, 0, 0, 0
2.var b = foo(0).foo(1).foo(2).foo(3)
通过1可知,b = foo(0).foo 此时产生新的闭包,闭包里面的变量为v1 = 0;
同理可得 b = foo(0).foo(1).foo 此时产生新的闭包,闭包里面的变量为v3 = 1
同理可得 b = foo(0).foo(1).foo(2).foo 此时产生新的闭包,闭包里面的变量为v3 = 2
所以最终输出:
undefined, 0, 1, 2
面试题1:
outside,无闭包
inside, 有闭包
面试题2: