【重點突破】——如何快速識別並解決“閉包問題”?
阿新 • • 發佈:2017-10-15
.com 不同的 ret log selector src spa 前端 混亂
一、引言
什麽是“閉包”?它既是前端程序中常常會碰到的一個千年大坑,也是這個大坑唯一可以解決自身的辦法。很多大牛對閉包都有自己的解釋,但每個人的解釋可能都不太一樣,看太多反而混亂,這裏,我會用一個小例子,盡量簡單的說明這個“閉包”到底是什麽,怎麽識別?如何解決?
二、閉包
- 什麽是? 一種對象,向外公開了特定的數據,以及操作這種數據的方法,供外部調用,就是閉包
- 為什麽?
- 全局變量:隨處可見,可反復使用。缺點:極易被汙染(易被篡改)。
- 局部變量:不會被汙染。缺點:僅函數內可用,且不可重用(不易被篡改)。
- 三大特點
- 外層函數
- 受保護的變量
- 內層函數
三、如何形成閉包
外層函數的作用域對象無法釋放,導致:外層函數的局部變量被保存下來(可以重用)。
- 第一步:將受保護的變量和操作變量的函數封裝在一個外層函數;
- 第二步:外層函數,要將內層函數隊形返回;
- 第三步:使用者調用外層函數,獲得內層函數對象。
四、快速識別
- 向外拋出對象,一定是閉包。
- 模型:循環函數嵌套一個內層函數對象返回變量值,外部調用此函數對象,得不到不同變量值的,一定是閉包問題。(依據閉包問題的原因,最後得到的值一定都是內層循環函數獲得的最後一個變量值) 。
五、例題說明
點擊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為實參
}
【重點突破】——如何快速識別並解決“閉包問題”?