翻譯:瘋狂的技術宅 medium.freecodecamp.org/a-basic-gui…
閉包是函式建立時作用域內所有變數的集合。要使用閉包,需要在另一個函式中建立一個函式,這種函式被稱為巢狀函式。內部函式可以訪問外部函式作用域中的變數(依靠閉包可以訪問外部函式作用域),即使在返回外部函式之後也是如此。每次建立巢狀函式時都會建立閉包。
在繼續瞭解閉包之前,首先了解一下JavaScript中的作用域鏈。
通常,有兩種型別的作用域:
- 全域性作用域
- 區域性作用域
在JavaScript中,函式內部的變數在外部是不可見的。但是在塊內的變數(if 或 while 之類)是可見的。
因此,JavaScript有函式作用域。沒有塊作用域。
var a = 10;
function app(){
var b = 2;
console.log(a); // 10
console.log(b); // 2
}
console.log(b); // ReferenceError: b is not defined
app();
複製程式碼
正像我們已知的那樣,a 是一個全域性變數並且 b 是一個區域性變數,它是app函式獨有的。
我們無法從區域性作用域之外獲取區域性變數的值。
使用巢狀函式 —— 函式內部的函式
var a = 10;
function app(){
var b = 2;
var d = 3;
function add(){
var c = a + b;
}
return add;
}
var x = app();
console.dir(x);
複製程式碼
在這裡app是父函式,add函式是子函式。
- 程式碼中沒有用 console.log 而是用了console.dir 來輸出指定JavaScript物件的所有屬性,這有助於開發人員獲取物件的屬性
- 變數 x 被分配給app函式,app函式返回add函式。因此我們可以看到add函式的所有物件屬性。
如果在瀏覽器中檢視控制檯,可以在Scopes陣列中看到Closure物件。
由於內部函式add訪問外部函式變數b 和 d,因此這2個變數將被新增到Closure物件中以供引用。
讓我們看看下一個例子:
var a = 10;
var startFunc;
function app(){
var b = 2;
function add(){
var c = a + b;
console.log(c);
}
startFunc = add();
}
app(); // 呼叫app函式
startFunc; // 上面呼叫的app函式會將add函式賦值給startFunc並輸出c的值
複製程式碼
- 一個名為 startFunc 的全域性函式被分配給add函式,該函式是 app 函式的子函式。
- 這隻有在呼叫 app 函式後才有可能,否則 startFunc 將作為全域性變數而不被分配任何值
在JavaScript中使用閉包
很多人在編碼時會用到閉包,但是不明白用它的原因。 JavaScript沒有像其他面嚮物件語言一樣的訪問修飾符,例如 private,public,protected。不過我們可以利用函式來保護名稱空間免受外部程式碼使用的影響。
特別是在函式中,**立即執行函式表示式(IIFE)**是在宣告之後會立即執行的函式表示式。在宣告函式之後,你不需要去呼叫該函式。
IIFE的語法定義是:
(function(){
//函式內部的變數和作用域
})();
複製程式碼
舉個例子:
var studnetEnrollment = (function () {
//私有變數,任何人都無法改變
//除了下面宣告的函式
var count = 0;
var prefix = "S";
// 返回一個命名函式表示式
function innerFunc() {
count = count + 1;
return prefix + count;
};
return innerFunc;
})();
var x = studnetEnrollment(); // S1
console.log(x);
var y = studnetEnrollment(); // S2
console.log(y);
複製程式碼
count和prefix是兩個私有變數,任何人都無法進行更改,只能訪問內部函式(即程式碼中的innerFunc)。只有名為閉包的功能才能對此進行訪問。
- 第一次呼叫studentEnrollment函式時,函式內的count變數由innerFunc函式遞增加1。
- 第二次,增加上一個計數值,即 1 增加到 2
- Closure功能可以實現這些功能。
結論
閉包是外部函式中的變數集合,它提供對內部函式作用域的訪問以保護全域性名稱空間。
閉包使開發人員能夠編寫像面嚮物件語言那樣的乾淨程式碼,這些程式碼不會混淆全域性和區域性變數的名稱。
編碼快樂...... !!!!!