对于JSer来说,闭包是一个难懂,但必须征服的概念,今天我们就来谈谈这个闭包…
1. 变量的作用域
在函数内部生命的局部变量在函数外面是访问不到的1
2
3
4
5
6var func = function(){
var a = 1;
console.log(a) //1
};
func();
console.log(a) //error函数内部的变量在函数体内找不到时,就会沿着
作用域链
往外层搜索1
2
3
4
5
6
7
8
9
10
11
12var b = 1;
var func1 = function(){
var c = 2;
var func2 = function(){
var d = 3;
console.log( c ); //2
console.log( b ); //1
};
func2();
console.log( d ) //error
};
func1();2. 变量的生存周期
对于在函数内用var声明的变量来说,退出函数时,这些局部变量就会被销毁,1
2
3
4
5var func3 = function(){
var e = 2; //退出函数后就会被销毁
alert(2);
};
func3();然而对于闭包来说
1
2
3
4
5
6
7
8
9
10
11var func4 = function(){
var a = 1;
return function(){
a++;
console.log(a);
}
};
var f = func4();
f(); //2
f(); //3
f(); //4
Why?
这是因为当执行var f = func4();时,f返回了一个匿名函数的引用,它可以访问到func4()被调用时产生的环境
,而局部变量一直处于这个环境中,既然局部变量所在的环境还能被外界访问,所以就有了不被销毁的理由
接下来我们来看一个闭包的经典应用
1 | <!DOCTYPE html> |
1 | <!-- js --> |
上面这个例子不管你点击哪个,它弹出的都是5,这是因为div节点的onclick事件是异步触发
的,当事件触发时,循环早已结束,此时的i就是5,那么如何来修改呢?看以下两种方法:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<!-- 方法一 -->
var nodes = document.getElementsByTagName('div');
for(var i=0,l=nodes.length;i<l;i++){
(function(i){
nodes[i].onclick = function(){
console.log(i)
}
})(i)
};
<!-- 方法二 -->
var nodes = document.getElementsByTagName('div');
for(var i=0,l=nodes.length;i<l;i++){
nodes[i].onclick = (function(i){
return function(){
console.log(i)
}
})(i)
}
- 3. 闭包的更多作用
- 1>. 封装变量
1 | var mult = function(){ |
上面的方法我们还可以加入缓存机制
,这样重复的就不用在计算了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19var mult = (function(){
var cache = [];
var calculate = function(){
console.log(arguments)
var a = 1;
for(var i=0,l=arguments.length;i<l;i++){
a *=arguments[i]
}
return a;
};
return function(){
var arg = [].join.call(arguments,',');
if(cache[arg]){
return cache[arg]
}
return cache[arg] = calculate.apply(null,arguments)
}
})()
console.log( mult(2,3,4) );
- 2>.延续变量‘寿命’
1 | var report = function(src){ |
如上情况,图片数据会丢失,因为并非每个http请求都是成功的,,又因为img是局部变量
,当函数调用完后,img就会销毁
,此时数据还没有传输完整
使用闭包修正如下:1
2
3
4
5
6
7
8
9var report = (function(){
var imgs = [];
return function(src){
var img = new Image();
imgs.push( img )
img.src = src;
}
})();
report('http://7xqjce.com1.z0.glb.clouddn.com/weixin.jpeg')
- 3>. 闭包与面向对象设计
对象以方法的方式包含了过程,而闭包在过程中以环境形式包含了数据,通常用面向对象能实现的功能,用闭包也能实现
1 | <!-- 闭包形式 --> |
1 | <!-- 面向对象形式 --> |