閉包和一部電影的關係

大眾美男典範發表於2018-08-16

在網上,關於閉包的文章眾多。

MDN文件中說:

閉包是函式和宣告該函式的詞法環境的組合

很多文章中說:

閉包是指有權訪問另一個函式作用域中的變數的函式

還有一篇文章,總結了閉包的四種定義

最後,我決定去請教我的一個經驗豐富的同事。

他說:

閉包就是閉著的包子

......

我發現閉包的最大難點,就是沒有一個明確的定義。

於是,我去其精華、取其糟粕,寫下這篇關於閉包但完全不去定義閉包的文章。

function outter(){
    var name = '小強'
    function inner(){
        console.log(name)
    }
    return inner
}
var foo = outter()
foo()   // '小強'
複製程式碼

上面這段程式碼,就是一個閉包。

無論閉包的定義是什麼,這段程式碼基本上是通行的

首先,如果套用這個定義:

閉包是指有權訪問另一個函式作用域中的變數的函式

那麼,函式inner就是閉包,因為我們知道:

定義在函式內部的函式,是可以訪問外部函式的作用域的。

簡寫一下:

function outter(){
    var name = '小強'
    function inner(){
        console.log(name)
    }
    inner()
}
複製程式碼

這種結構下,inner函式還是有權訪問outter函式作用域中的變數的,所以這是不是閉包?

(我也不知道)

上面程式碼,是一種最常見的函式巢狀。

outter函式執行時,會建立一個屬於outter的執行環境及變數物件。

inner函式執行時,又會建立一個inner的執行環境及變數物件。

它們的相同點是:

執行完畢之後,各自的執行環境及變數物件都會被銷燬

儘管函式是一等公民,但是它們執行完畢後、變得“沒用”,JS很快將它們“滅門”,這就是JS垃圾回收機制。

它們的聯絡是:

inner函式可以訪問到outter函式的變數物件。

變數物件,顧名思義,就是儲存該函式自身變數的一個物件。

內部函式儲存所有外層函式的變數物件,形成了自己的作用域。

inner函式的作用域,包括自身的變數物件、outter的變數物件和window的變數物件。

為什麼要儲存別人的變數物件?

因為對自己有用,自身沒有的話就可以去用外層的。

可以說,外層函式的變數服務於內部函式。

再回到這種形式:

function outter(){
    var name = '小強'
    function inner(){
        console.log(name)
    }
    return inner
}
var foo = outter()
foo()   // '小強'
複製程式碼

不同於普通巢狀,

這裡當outter函式執行到最後時,將inner函式return了出去。

顯然,outter已經執行完畢了,但是它的執行環境及變數物件都被銷燬了嗎?

並不是。

有一個倖存者,就是**outter函式的變數物件**。

雖然outter函式在return之後,自身已經執行完畢。

但是,因為它return的是巢狀在自己內部的函式inner,並賦值給全域性變數foo,這就導致:

  • 一方面,outter函式執行完畢,outter的變數物件理應被銷燬

  • 另一方面,inner函式被賦值給全域性變數,隨時有可能被呼叫,那它的作用域不應該被破壞,其中的outter變數物件也就不該被銷燬

上面已經說過:

內部函式儲存所有外層函式的變數物件,形成了自己的作用域

所以,就是因為還有用,所以outter函式的變數物件並沒有在outter執行後被銷燬,成為倖存者。

最終,當我執行foo()的時候,

儘管outter函式早已執行完畢,但依然可以列印出其變數name的值'帥哥小強'。

而我想到的,是《辛德勒名單》這部電影。

1939年,波蘭在納粹德國的統治下,黨衛軍對猶太人進行了隔離統治。

這時,德國商人奧斯卡·辛德勒和德軍建立了良好的關係,他的工廠僱用猶太人工作,大發戰爭財。

猶太人遭到了德軍的大屠殺,辛德勒目睹了這一切之後十分震撼。

辛德勒讓自己的工廠成為集中營的附屬勞役營,在那些瘋狂屠殺的日子裡,他的工廠也成為了猶太人的避難所。

德國戰敗前夕,屠殺猶太人的行動越發瘋狂,辛德勒向德軍軍官開出了1200人的名單,傾家蕩產買下了這些猶太人的生命。

這個電影很有名,如果沒看過建議看一下。

同樣,在我們的JS世界中:

當一個函式執行完畢,它的執行環境及變數物件也會遭到一場屠殺,即垃圾回收機制。

在這場屠殺中,辛德勒用一份自己工廠員工的名單,使自己的工廠成為集中營的附屬勞役營,更成為猶太人的避難所。

inner函式,也有一份自己員工的名單,那就是作用域。

這份名單上,就包含了outter函式的變數物件。

inner函式被賦值給全域性變數,就好比辛德勒和德軍建立了良好關係,

它的作用域就成為變數物件的避難所,

因為outter函式的變數物件被寫在inner函式的員工名單(即作用域)中,所以才免遭殺害。

這就是JS版的《辛德勒名單》。

那麼在這個過程中,究竟哪部分屬於閉包呢?

相關文章