閉包

概念

簡單理解:函數中的函數,就是閉包。也可以說是:有權訪問另一個函數作用域中的變量的函數。

  1. 閉包也是函數,只不過比較特殊。

  2. 閉包的優點,也是它的缺點:可以把局部變量駐留在內存中,因此可以避免使用全局變量。當代碼量足夠大時,使用全局變量會發生命名的沖突、各個地方的對全局變量的修改而導致的一系列問題。

  3. 由于閉包會導致局部變量不被回收,可能會消耗內存。因此,在閉包使用結束后,應將閉包設置為 null

function f(){
    var age = 100;
    return function(){
        age++;
        return age;
    }
}
var fi = f();
for(var x = 0;x<10;x++){
    console.log(fi())
}
fi = null;//回收閉包
  1. 使用閉包時,閉包的外部函數應該只調用一次。因為每調用一次外部函數,函數中的局部變量就會被重新初始化。上例中,只調用了一次 f(),保證 age 只被寢化一次。

  2. 各個閉包函數共享外部函數的所有變量,一個函數對變量進行修改,所有的閉包函數都會隨之變化

    function Outer(value){
        var a = value; 
        m1 = function(){
            console.log('m1.a = '+a)
            a = 'm1'
        }
        m2 = function(){
            console.log('m2.a = '+a)
            a = 'm2'
        }
    }
    Outer('init')
    
    m1() // m1.a = init
    m2() // m2.a = m1
    m1() // m1.a = m2
    

    m1,m2 各輸出當前 a 的值,并進行修改。可以發現,m2 輸出的就是 m1 修改后的值。而 m1 輸出的也是 m2 修改后的值。

取值問題

在下面代碼中,輸出的結果是 5。

function Fun () {
    var r = [];
    for(var x = 0;x<5;x++){
        r[x] = function(){
            return x;
        }
    }
    return r;
}
console.log(Fun()[0]()) // 輸出的是 5

這是因為在執行完 Fun() 時,函數內部的循環已經執行完畢,x 值也變成了 5。所以在執行 r 的第一個函數時,取到 x 值就是 5。
可以改成如下形式:


function Fun () {
    var r = [];
    for(var x = 0;x<5;x++){
        r[x] = (function(num){
            return function inner(){
                return num
            }
        })(x) // 自動執行匿名函數
    }
    return r;
}
var r = Fun()
for(var x = 0;x<5;x++){
    console.log(r[x]()) // 依次輸出0到4
}

匿名函數執行時,會將 x 值傳遞到匿名函數中,而匿名函數內部有一個閉包,會將傳入的 x 值駐留在內存中。這導致每一個匿名函數內部的閉包對應不同的 num 值。因此,在輸出是輸出的是 0 到 4。

閉包中的 this

由于閉包也是一個函數,所以它中的 this 指向了運行該函數的對象。

如:

var age = "window.age"
var o = {
    age:'o.age',
    getThis:function(){
        return this.age;
    }
}
console.log(o.getThis()) // a.age

var o2 = {
    age:'o2.age',
    getThis:function(){
        return function(){
            return this.age;
        }
    }
}
console.log(o2.getThis()()) // window.age

var o3 = {
    age:'o3.age'
}
console.log(o2.getThis().call(o3)) //o3.age

第二次輸出時,相當于在 window 下直接運行了閉包,所以閉包中的 this 指向了 window 中的 age 屬性。

第三次輸出時,通過對象冒充的方式,使得閉包在 o3 對象中運行,所以閉包中的 this 指向了 o3 中的 age 屬性。

指向外部函數

由于閉包執行時,this 指向的是調用閉包的對象。如果想讓 this 指向閉包的外部函數,可以如下修改:

var o2 = {
    age:'o2.age',
    getThis:function(){
        var that = this; // 用 that 變量記錄執行外部函數時 this 的指向
        return function(){
            return that.age; // 閉包,保證 that 變量的全局性
        }
    }
}
console.log(o2.getThis()()) // o2.age

在外部函數中使用一個變量記錄外部函數的 this,并在閉包中使用該變量代替
this
,由于閉包的特性,可以保證該變量能常駐內存,所以在執行閉包時該變量就表示了外部函數的 this 。

模仿塊級作用域

js 中沒有塊級作用域。if for ,while 等語句執行完畢后,{} 內的變量外界依舊可以訪問。但,函數內部的變量卻是局部變量。所以, 可以使用匿名函數模仿塊級作用域——使用一個自執行的匿名函數將要分塊代碼包裹起來即可

for(var i = 0;i<10;i++){}
console.log(i); // 10

(function(){
    for(var x = 0;x<10;x++){}
})() 
console.log(x) // 此處訪問 x 出錯
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • ● 閉包基礎 ● 閉包作用 ● 閉包經典例子 ● 閉包應用 ● 閉包缺點 ● 參考資料 1、閉包基礎 作用域和作...
    lzyuan閱讀 960評論 0 0
  • 閉包(closure)是Javascript語言的一個難點,也是它的特色,很多高級應用都要依靠閉包實現。 一、變量...
    zock閱讀 1,084評論 2 6
  • 閉包: 官方”的解釋是:閉包是一個擁有許多變量和綁定了這些變量的環境的表達式(通常是一個函數),因而這些變量也是該...
    小裁縫sun閱讀 629評論 0 5
  • 作用域和閉包是 JavaScript 最重要的概念之一,想要進一步學習 JavaScript,就必須理解 Java...
    劼哥stone閱讀 1,198評論 1 13
  • 這是近日里的一則新聞熱點。 > 楚天都市報6月2日訊:武漢名校女博士機場掌摑工作人員,將面臨10天拘留。(記者劉閃...
    圣易王時閱讀 241評論 0 0