深入理解javascript系列(七):閉包(1)

Panthon發表於2018-06-13

閉包(Closures)是我學習過程中的一個瓶頸。

沒有跨不過去的坎,只有走不出的心。

7.1  概念

閉包是一種特殊的現象。

它由兩部分組成=>執行上下文(A),以及在該執行上下文中建立的函式(B)。

當B執行時,如果訪問了A的變數物件中的值,那麼閉包就會產生。

許多書籍、文章裡都以函式B的名字指代這裡生成的閉包。而在Chrome中,則以執行上下文A的函式名代指閉包。

我們只需知道,一個閉包物件,有A、B共同組成,在以後的篇幅中,都將以Chrome的標準來稱呼。

//demo01.js
function foo() {
    var a = 20;
    var b = 30;
    
    function bar() {
        return a + b;
    }
    return bar;
}
var bar = foo();
bar();複製程式碼

在上面的例子中,首先執行上下文foo,在foo中定義了函式bar,而後通過對外放回bar的方式,得以讓bar執行。當bar執行時,訪問了foo內部的變數a和b。因此這個時候閉包就產生了。

在Chrome中通過斷點除錯的方式可以逐步分析該過程,從下圖中可以看出,此時閉包的產生,用foo代指。

深入理解javascript系列(七):閉包(1)

在圖中,箭頭所指的正式閉包。其中Call Stack為當前的函式呼叫棧,Scope為當前正在被執行函式的作用域鏈,Local為當前活動物件(AO)。

在學習了閉包的基礎概念後,下面就來驗證一下你是否真的理解了。現在思考一個小問題,把上面的程式碼稍作改動,是否形成閉包了?

//demo02.js
function foo() {
    var a = 20;
    var b = 30;
    
    function bar() {
        return a + b;
    }
    bar();
}
foo();複製程式碼

仍然是foo中定義的bar函式在執行時訪問了foo中的變數,因此這個時候仍然會形成閉包。如圖所示:

深入理解javascript系列(七):閉包(1)

在來看一個非常有意思的例子。

//demo03.js
function add(x) {
    return function _add(y) {
        return x + y;
    }
}
add(2)(3);複製程式碼

對於這個既熟悉又陌生的函式,他有閉包產生嗎?

當然有。當內部函式_add被返回撥用時,訪問了add函式變數物件中的x,這個時候,閉包就會產生,如下圖所示。一定要記住,函式引數中的變數傳遞給函式之後也會加到變數物件中。

深入理解javascript系列(七):閉包(1)

還有一個例子可以驗證大家對於閉包的理解。

看看下面這段程式碼中是否有閉包產生。

//demo04.js
var name = 'window';

var p = {
    name: 'pan',
    getName: function() {
        return function() {
            return this.name;
        }
    }
}
var getName = p.getName();
var _name = getName();
console.log(_name);

複製程式碼

getName在執行的時候,其this是指向window物件的,而這個時候並沒有形成閉包的環境,因此這個例子沒有閉包。

那麼如果按照下面的方式進行改動呢?

//改動一
//demo05.js
var name = "window";

var p = {
    name: 'pan',
    getName: function() {
        return function() {
            return this.name;
        }
    }
}

var getName = p.getName();

var _name = getName.call(p);    //利用call的方式改變其this指向

console.log(_name);

複製程式碼

//改動二
//demo06.js
var name = "window";

var p = {
    name: 'pan',
    getName: function() {
        var self = this;           //利用變數儲存的方式保證其訪問的是P物件
        return function() {
            return self.name;
        }
    }
}

var getName = p.getName();

var _name = getName();

console.log(_name);
複製程式碼

上面兩處改動分別利用call與變數儲存的方式保證了this的指向,那麼,它們哪一個產生了閉包哪一個沒有產生閉包了?這個問題,希望您與我共同思考。

這些都是我以往的學習筆記。如果您看到此筆記,希望您能指出我的錯誤。有這麼一個群,裡面的小夥伴互相監督,堅持每天輸出自己的學習心得,不輸出就出局。希望您能加入,我們一起終身學習。歡迎新增我的個人微訊號:Pan1005919589


相關文章