當我們宣告且呼叫一個函式時,函式本身會形成一個 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裡面設計迴圈或定時執行的函式