在談到閉包前,要先談談範圍鏈
範圍鏈 Scope Chain
在 ES6 以前,變數透過 var 宣告,當時切分變數的最小單位為「函式」,有以下特性需要留意:
- 內層可以取得外層的變數,但外層無法取得內層變數
- 在「定義」函式時就決定了範圍鏈,而非執行時
💥 範圍鏈地雷
- 確認範圍是否有使用 function 切分
- 若沒有宣告變數,即使在函式內,都會變成全域變數(最外層)
範例
let a = "A";
function fn1() {
let b = "B";
fn2();
function fn2() {
let c = "C";
//可以取得外層所有變數
console.log(a, b, c);
}
}
fn1();
//error,因為無法取得 b,c 變數
console.log(a, b, c);
認識完範圍鏈,就來看看閉包吧!
閉包 Closure
用途
- 可避免產生全域變數,同時避免開發者變數命名衝突,是透過「內層可以向外取得外層變數、外層無法取得內層變數」的特性達成,也就是範圍鏈的概念
- 當內部函式回傳時,等同取得內部函式當下環境的變數值(可以取得外層函式的變數值
作法
假設 a 是外部函式,b 是存在於 a 裡面的內部函式
- b 不會有自己的變數,但因為作用域的原理,可以取得 a 的變數
- 設定 a 函式會呼叫 b,就可以在 b 裡面針對 a 建立的變數進行操作,避免全域變數產生
- 之後開發者只需呼叫 a ,a 就會去呼叫 b
範例
以往做計數器時,會在全域環境宣告一個變數來記錄數字,會出現全域變數,造成多個開發者之間命名衝突
//全域變數
let count = 0;
function add() {
return ++count;
}
console.log(add());
使用閉包寫法
function addFunc() {
let count = 0;
function add() {
return ++count;
}
return add;
}
//addFunc() 會回傳 add() 整個 function
//之後呼叫 result 等同於呼叫 add() 這個內部函式
let result = addFunc();
console.log(result());
注意:若缺少 let result = addFunc()
這個動作,直接寫 addFunc()
,回傳的會是 add 整個 function,而不是計算結果
希望看完這篇有更認識閉包這個觀念,若有錯誤也歡迎指正,謝謝看到這裡的大家。