當我們宣告且呼叫一個函式時,函式本身會形成一個 scope,scope 裡面會包含 Closure

特性

Closure 原則就是,是一個可以用 by Reference 的方式紀錄函式建立時的環境,包括了:

Closure 內部所建立的變數,限定於內部使用,

在Closure 裡也可以存取到 Scope chain 中的所有變數,

用途 - 模擬私有方法

因此,藉由這些特性,我們可以使用 closure 模擬私有方法

例如,我們常用這樣的特性,以 IIEF (立即呼叫函式表達式) 的方式來建立 Module (模組),

模組內就能建立類似物件的私有屬性及方法

用途 - 柯里化(Currying)

Closure 裡的變數被記憶,同時也可以取得外部的變數

使用 closure 結構,

把原本多個傳入參數的函式,轉變為一次只傳入一個參數的函式,可以連續呼叫使用。

Closure 裡的 this

函式的 Closure 雖然也包含了this,但是不會自動去記憶,

因為 this 不在 scope chain 裡。

在使用內部函式時,他也有自己的this,

因此,通常要先用變數指定this,讓內部函式使用

常見錯誤 : 在Closure 裡使用迴圈+延遲執行的function

迴圈 + 具有延遲執行的function 例如 setTimeout( callback_function, 時間)

如果再 closure 裡面使用這樣的方式,當 closure 內文執行完畢, 迴圈也跑完了,所有迴圈 i 都已經跳出迴圈執行,

而延遲執行的 function 卻還未執行,由於 closure 是以 by Referance 的方式儲存變數,

當 callback function 終於準備執行時,所有 i 都已經變成迴圈執行完畢的最終i值

要避免這種情況,就要把這個延遲 callback 建立新的作用域環境

留意 Closure 效能問題

其實,在函式內建立函式,並不明智,因為從速度和記憶體角度而言,它都會影響腳本性能。

但是

一般情況,javascript 有很好的垃圾回收機制,當function執行完畢就會動回收裡面的變數

但是,Closure是一種高消費的語法,

當定義過多層的巢狀 Closure,會造成搜尋的時間越長,

每當使用或呼叫函式時,它需要尋遍整個scope chain與記憶環境,越多層負擔越重

因此不要再巢狀 closure裡面設計迴圈或定時執行的函式