一、什么是闭包?
- 闭包就是一个函数,它处于另一个函数中,并且可以访问它的外部函数中的变量。
function fun(){
var n=0;
return function(){ //这就是一个闭包,它可以使用变量n
n++;
console.log("n="+n);
}
}
var f=fun();
f(); //输出:n=1
f(); //输出:n=2
二、闭包有什么作用?
- 访问外部函数的变量。
- 防止外部函数执行完之后,变量被销毁。
- 加强封装性。
三、闭包的缺点?
- 由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,很容易造成内存泄漏。
四、闭包的应用场景
1. 通过循环给页面上多个dom节点绑定事件,点击编号为几的节点则弹出它对应的编号
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
如果不使用闭包:
for(var i = 0; i < list.length; i++) {
list[i].addEventListener('click', function(){
alert(i)
}, true)
}
由于点击事件是异步触发的,当事件被触发时,for循环以及结束了,此时变量i的值已经是5!所以当点击事件顺着作用域链从内向外查找变量i时,找到的值总是5,所以无论点击哪一项都会弹出5。
下面用闭包改写:
var list = document.getElementsByTagName('li')
for(var i = 0; i < list.length; i++) {
list[i].addEventListener('click', function(i){
return function() {
alert(i+1)
console.log(i+1)
}
}(i), true)
}
改成闭包之后,每次循环的i值都被封闭起来,这样在函数执行时,会查找定义时的作用域链,这个作用域链里的i值是在每次循环中都被保留的,因此点击不同的li会alert出不同编号。
2. setTimeout
原生的setTimeout传递的第一个函数是不能带参数的:
setTimeout(fun(),1000);
因此,闭包就派上用场了:
function fun(num){
var age = num;
return function(){
console.log(age);
}
}
var getAge = fun(200);//传入需要的参数,得到函数(闭包)的引用
var age = setTimeout(getAge,1000);//正确输出
3. 使用闭包访问私有变量
function Fun(){
var name = 'tom';
this.getName = function (){
return name;
}
}
var fun = new Fun();
console.log(fun.name);//输出undefined,在外部无法直接访问name
console.log(fun.getName());//输出tom
最后,觉得一位大佬说的很好:
闭包这东西,举个不恰当的例子:就像Java里常说的设计模式。
很多人痴迷于各种设计模式,就想方设法硬是用到工作中,然后就觉得高大上了,而不管合适不合适。实际上,那些前人总结出来的设计模式,确实是精华,确实值得学习。但是不管合适不合适、需要不需要地直接拿来用是不对的。我们应该吸收里面的思想而不是形式,等你代码写多了,经验多了,就会不经意地用上,甚至自创出之前没有但是更适合你自己的模式。
闭包也一样,闭包只是个术语而已,我们应该去了解它形成的原理,它的形成背后的知识点,比如作用域链、变量解析、词法环境等等,从而真正理解为什么出现闭包这种现象。而不是觉得它高大就必须把它用到工作中。等你代码写多了,涉猎范围广了,自然会不知不觉用上它。真的,到那时候,很可能你代码里出现了闭包,但是你却没有意识到。