0%

理解闭包

1
2
3
4
5
6
7
8
9
function foo(){
var a=2;
function bar(){
console.log(a);
}
return bar;
}
var baz=foo();
baz();//2

内部函数bar()能够访问foo()的内部作用域,将bar()函数本身作为值类型传递,调用foo(),我们通常希望整个内部作用域被销毁,然而因为bar()所声明的位置拥有涵盖foo()内部作用域的闭包,使得作用域一直存活,以供bar()在任何时间之后进行引用。内部函数依然持有对外部函数的作用域的引用,这个引用就叫做闭包

总结来说:本质就是上级作用域内变量的生命周期,因为被下级作用域内引用,而没有被释放。就导致上级作用域内的变量,等到下级作用域执行完以后才正常得到释放。

闭包的作用:

封装变量

闭包可以把一些不需要暴露在全局的变量封装成“私有变量”,如果一个大函数中有一些代码块能独立出来,我们常常把这些代码块封装在一个独立的小函数里面,独立出来的小函数有助于代码复用,如果这些小函数不需要在程序的其他地方使用,可以用闭包封闭起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var mult = (function(){
vat cache = {}
var calculate = function(){
var a=1
for(var i=0,l=arguments.length;i<l;i++){
a = a*arguments[i]
}
}
return function(){
var args = Array.prototype.join.call(arguments,',')
if(args in cache){
return cache[args]
}
return cache[args] = calculate.apply(null,arguments)
}
})()

延长局部变量寿命

1
2
3
4
5
var report = function(src){
var img = new Image()
img.src = src
}
report("http://xxx.com/getUserInfo")

如果report函数调用结束后,img局部变量立即被销毁,如果还没来得及发起HTTP请求,此次起笔趣就会丢失掉

用闭包延长Img寿命

1
2
3
4
5
6
7
8
9
var report = (function(){
var imgs = []
return function(src){
var img = new Image()
imgs.push(img)
img.src = src
}

})()

闭包和面向对象设计

对象以方法的形式包含了过程,而闭包则是在过程中以环境的形式包含了数据通常面向对象的思想能实现的功能闭包也能实现:

1
2
3
4
5
6
7
8
9
10
var extent = function(){
var value = 0;
return {
call:function(){
value++;
console.log(value);
}
}
}
var extent = extent()

用面向对象的思想:

1
2
3
4
5
6
7
8
var extent = {
value:0,
call: function(){
thie.value++;
console.log(this.value)
}

}

用闭包实现命令模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var TV = {
open: function(){
console.log('open')
} ,
close: function(){
console.log('close')
}

}
var createCommand = function(receiver){
var excute = function(){
return receiver.open()
}
var undo = function(){
return receiver.close()
}
return {
excute:excute,
undo: undo
}
}
var setCommand = function(command){
document.getElementById('excute').onclick = function(){
command.excute()//open
}
document.getElementById('undo').onclick = function(){
command.undo()//close
}

}
setCommand(createCommand(Tv))

TV即命令接收者,往函数对象中预先植入命令接收者,命令接收者被封闭在闭包形成的环境中

闭包与内存管理:

使用闭包的时候容易造成循环引用,但这并非闭包的问题,浏览器的采用引用计数的垃圾回收策略,如果两个对象发生了循环引用,那么这两个对象肯都无法被回收,但是循环引用造成内存泄漏本质上也不是闭包的原因,这时需要把循环引用的变量中的其中一个设为null