_閉包_是 JavaScript 中的一種程式設計模式,它讓外層語彙作用域的變數可以在巢狀的程式區塊內使用。JavaScript 以透明的方式支援閉包,因此我們常常在不知道它們是什麼的情況下就用到了它們。
// Top-level declarations are global-scope
const dozen = 12;
// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
return dozen * n;
}
_閉包_這個名稱在歷史上源自λ演算,並由 Scheme 推廣(來源),用來指稱函式中開放與封閉的變數綁定。
資料隱私 / 資料封裝
// Consider this function:
const saveNumber = (number) => {
// The saved value is unaccessible by the outer lexical scope.
const value = number;
// We can provide access to the primitive value with a function, but the original will never change
return () => value;
};
// Attempting to set the variable outside of its lexical scope results in an error
value = 42;
部分應用
// The arguments are applied one-by-one, using closures to arrive at the final result
function partialBuildUri(scheme) {
return function (domain) {
return function (path) {
return `${scheme}://${domain}/${path}`;
};
};
}
// A function could apply them all at once.
function buildUri(scheme, domain, path) {
return partialBuildUri(scheme)(domain)(path);
}
// Or apply a few to create a flexible system of functions for reuse.
function buildHttpsExercismUri(path) {
return partialBuildUri('https')('exercism.org');
}
在任何程式語言中,閉包都與變數作用域密切相關。在 JavaScript 中,變數的語彙作用域有 3 種:
var關鍵字宣告的變數。{...})中使用let和const關鍵字宣告的變數。在實務上,閉包通常出現在函式(或區塊)使用外層作用域的變數時。在 JavaScript 中,這是透明支援的,所以閉包常在不被特別提及的情況下使用。
var關鍵字定義的是函式作用域的變數。這表示用var定義的變數,在定義它的函式中任何位置,以及任何巢狀的語彙作用域(無論是函式或區塊)都可以使用。
{
var five = 5;
}
// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;
let和const關鍵字定義的是區塊作用域的變數。let定義的是可變的變數,它的值可以設定多次,也可以只定義而不初始化。const定義的變數必須在定義時初始化,之後它的值就不能再改變。如果const變數被設為原始值,它就不能被修改;如果const變數被設為參照值,例如_陣列_或_物件_,那麼參照本身不能改變,但陣列的內容可以被修改。
來看看這個區塊:
// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;
{
// This succeeds, as 'two' is available from the outer lexical scope
const oneLess = two - 1;
}
// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);
雖然我們可以任意用大括號({...})來建立區塊,但最常看到它們出現在_條件式_、_函式_和_類別_的周圍。
const PI = 3.14;
// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
return PI * radius * radius;
}