重点突破——如何快速识别并解决“闭包问题”?
Posted 宁静致远
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了重点突破——如何快速识别并解决“闭包问题”?相关的知识,希望对你有一定的参考价值。
一、引言
什么是“闭包”?它既是前端程序中常常会碰到的一个千年大坑,也是这个大坑唯一可以解决自身的办法。很多大牛对闭包都有自己的解释,但每个人的解释可能都不太一样,看太多反而混乱,这里,我会用一个小例子,尽量简单的说明这个“闭包”到底是什么,怎么识别?如何解决?
二、闭包
- 什么是? 一种对象,向外公开了特定的数据,以及操作这种数据的方法,供外部调用,就是闭包
- 为什么?
- 全局变量:随处可见,可反复使用。缺点:极易被污染(易被篡改)。
- 局部变量:不会被污染。缺点:仅函数内可用,且不可重用(不易被篡改)。
- 三大特点
- 外层函数
- 受保护的变量
- 内层函数
三、如何形成闭包
外层函数的作用域对象无法释放,导致:外层函数的局部变量被保存下来(可以重用)。
- 第一步:将受保护的变量和操作变量的函数封装在一个外层函数;
- 第二步:外层函数,要将内层函数队形返回;
- 第三步:使用者调用外层函数,获得内层函数对象。
四、快速识别
- 向外抛出对象,一定是闭包。
- 模型:循环函数嵌套一个内层函数对象返回变量值,外部调用此函数对象,得不到不同变量值的,一定是闭包问题。(依据闭包问题的原因,最后得到的值一定都是内层循环函数获得的最后一个变量值) 。
五、例题说明
点击1,2,3按钮,获得对应按钮值,有如下代码:
<body>
<h3>JS中的闭包陷阱</h3>
<button>1</button>
<button>2</button>
<button>3</button>
<script>
var list = document.querySelectorAll(\'button\');
for(var i=0;i<list.length;i++){
var b = list[i];
b.onclick = function(){
console.log(i);
}
};
</script>
</body>
打印结果:
坑:这属于典型的闭包问题,打不出0,1来,全部为3。
原因:变量i就1个,并且这段代码不仅是对外公开了一个变量i,还公开了三个不同的监听函数,分别绑定给不同的按钮。
如果看所有JS调用完成(函数外打印),i的值,如下:
console.log(\'JS调用完成,i=\'+i);
结果:
所以:此时所有JS都已调用完成,i的值等于3。但是,按钮的事件监听并没有调用,可它们都要用i,因此,这个时候,再手动调用事件监听,所获得的i值只能是3。
六、解决方法
闭包问题必须要靠闭包方案来解决
- 把原本的一个闭包,拆分成三个闭包(复杂)
- 匿名函数,转化为有名函数(简单)
//方法一:把原本的一个闭包拆为三个
for(var i=0;i<list.length;i++){
var b = list[i];
b.onclick = outer(i);
}
function outer(num){
function inner(){
console.log(num);
}
return inner;
}
//方法二:匿名函数变成有名函数
for(var i=0;i<list.length;i++){
var b = list[i];
b.onclick = (function(num){//外部函数--此处传递形参num,注意不能再取i为变量名,否则又会重复
return function(){//内部函数
console.log(num);
}
})(i);//闭包上下文变量i,实参
}
结果:
如果打印btn,实现代码和效果如下:
for(var i=0;i<list.length;i++){
var b = list[i];
b.onclick = (function(btn){//外部函数-
return function(){//内部函数
console.log(btn);
}
})(b);//传递b为实参
}
注:转载请注明出处
以上是关于重点突破——如何快速识别并解决“闭包问题”?的主要内容,如果未能解决你的问题,请参考以下文章
重点突破—— Nodejs+Express+MongoDB的使用基础