1. 程式人生 > >【重點突破】——如何快速識別並解決“閉包問題”?

【重點突破】——如何快速識別並解決“閉包問題”?

.com 不同的 ret log selector src spa 前端 混亂

一、引言

什麽是“閉包”?它既是前端程序中常常會碰到的一個千年大坑,也是這個大坑唯一可以解決自身的辦法。很多大牛對閉包都有自己的解釋,但每個人的解釋可能都不太一樣,看太多反而混亂,這裏,我會用一個小例子,盡量簡單的說明這個“閉包”到底是什麽,怎麽識別?如何解決?

二、閉包

  • 什麽是? 一種對象,向外公開了特定的數據,以及操作這種數據的方法,供外部調用,就是閉包
  • 為什麽?
  1. 全局變量:隨處可見,可反復使用。缺點:極易被汙染(易被篡改)。
  2. 局部變量:不會被汙染。缺點:僅函數內可用,且不可重用(不易被篡改)。
  • 三大特點
  1. 外層函數
  2. 受保護的變量
  3. 內層函數

三、如何形成閉包

外層函數的作用域對象無法釋放,導致:外層函數的局部變量被保存下來(可以重用)。

  • 第一步:將受保護的變量和操作變量的函數封裝在一個外層函數;
  • 第二步:外層函數,要將內層函數隊形返回;
  • 第三步:使用者調用外層函數,獲得內層函數對象。

四、快速識別

  • 向外拋出對象,一定是閉包。
  • 模型:循環函數嵌套一個內層函數對象返回變量值,外部調用此函數對象,得不到不同變量值的,一定是閉包問題。(依據閉包問題的原因,最後得到的值一定都是內層循環函數獲得的最後一個變量值) 。

五、例題說明

點擊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。

六、解決方法

閉包問題必須要靠閉包方案來解決

  1. 把原本的一個閉包,拆分成三個閉包(復雜)
  2. 匿名函數,轉化為有名函數(簡單)

//方法一:把原本的一個閉包拆為三個
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為實參
}

技術分享

【重點突破】——如何快速識別並解決“閉包問題”?