1. 程式人生 > >什麼是閉包?從底層解釋閉包的執行

什麼是閉包?從底層解釋閉包的執行

之前看過廖雪峰老師的閉包講解,當時不明白其中的執行原理,今天在看了渡一的js課程後,明白了閉包的原理。那就舉兩個例子來解釋下。

首先要了解預編譯中的幾個名詞:

GO物件(Global Object),全域性環境下建立的執行期上下文,就是全域性作用域。 AO物件(Activation Object),也叫執行期上下文。在函式被執行前一刻建立,可以稱為是區域性環境、區域性作用域。 Scope chain(作用域鏈),被建立的區域性作用域會繼承父級的作用域,形成一條作用域鏈。

在一個函式的執行前有個預編譯,步驟如下

1.建立AO物件(Activation Object),執行期上下文。 2.尋找函式的形參和內部的變數宣告,將變數宣告和形參名作為AO屬性名,如果有重複則跳過,初始值為undefined。 3.將實參的值放到形參裡面去,將實參值和形參統一。函式內變數宣告的值傳遞到AO對應的屬性名。 4.在函式體內部尋找函式宣告,建立AO屬性名,屬性值為函式體。 5.GO除了沒第三步,其它都和AO一樣。 接下來看廖雪峰老師,講python閉包那節課中的兩個例子

def count():
    fs = [ ]
    for i in range(1, 4):  
        def f():                             
             return i*i
        fs.append(f)
    return fs

f1, f2, f3 = count()
--------------------------------
>>> f1()
9
>>> f2()
9
>>> f3()
9

按上面的理論來看,在for迴圈結束後,原本的fs = [ ]從一個空陣列變成了fs = [ f , f , f ],裡面是一樣的函式名。 並且在迴圈體結束後,i = 3。 此時 f1, f2, f3 = count(),所繼承的東西是一樣的,都是函式名 f。 因為函式b被return丟擲來了,它帶著a的AO和GO,而在a的AO裡i = 3。 所以在執行f1, f2, f3時,結果就都等於9了。

然後看第二個例子:

def count():
    def f(j):
        def g():
            return j*j
        return g
    fs = []
    for i in range(1, 4):
        fs.append(f(i))   # f(i)立刻被執行,因此i的當前值被傳入f()
    return fs
    
f1, f2, f3 = count()    
---------------------------------
>>> f1()
1
>>> f2()
4
>>> f3()
9

在count的AO裡,空陣列從fs =[ ]變成了fs = [ f(1) , f(2) , f(3) ]。 先不繼續將函式f,g的AO列出來。 直接從return fs開始。這時候f1, f2, f3從陣列中被賦予了三個不同的函式,這三個函式是以陣列的形式,被閉包返回了出來,這三個函式都得到了count的AO和GO

當執行f1, f2, f3時,其實執行的是被返回的g,函式g執行的就是j * j這個表示式。 而由於給陣列末尾新增的是f (i),也就是 f(1) , f(2) , f(3) ,將實參替換形參,就得出了1,4,9的結果。

底層的執行大概就是這樣了,但還有很多東西沒有寫出來,閉包最重要的就是弄明白AO和GO的執行步驟、作用域鏈繼承,這些懂了,閉包就沒問題了。

閉包的作用: 1.實現共有變數。 2.可以用作快取。 3.可以實現函式封裝,及屬性私有化。