# 閉包 Closure
1. 閉包是一種在計算機科學和程式設計中的概念,通常與函數相關。
2. 它捕獲了函數定義時所在的作用域內的變數,並允許這些變數在函數執行時仍然可用,
即使它們的作用域已經結束。
## 形成方式
1. 函數內部定義函數
當在一個函數內部定義另一個函數時,內部函數可以捕獲外部函數作用域內變數,
只要內部函數實際上使用了外部函數的變數,內部函數及其捕獲的變數就形成了一個閉包。
如果內部函數不使用外部函數的變數,則不會形成閉包。
2. 函數作為參數傳遞或返回值
如果一個函數接受另一個函數作為參數,並在內部使用它,
或者一個函數將另一個函數作為返回值返回,
那麼該內部函數通常會捕獲包含它的外部函數的作用域,形成閉包。
這種情況通常會形成閉包,因為內部函數需要訪問外部函數的作用域內的變數,以便正確執行。
```
// JavaScript程式碼示例
// 在outer的作用域內定義了一個內部函數inner
// 內部函數捕獲了外部函數的局部變數x,閉包包含了變數x、inner函式
function outer(x) {
function inner(y) {
return x + y;
}
return inner; // 外部函數返回內部函數形成閉包
}
const closure = outer(10); // 調用outer,得到一個閉包
console.log(closure(5)); // 調用閉包,輸出15,因為它仍然可以訪問x的值
```
## 使用情境
閉包的存在使得在某些情況下可以實現高級的程式設計技巧,例如函數工廠(function factory)、私有變數(private variables)和記憶化(memoization)。
### 封裝數據和行為
通過使用閉包,您可以創建一個函數,該函數封裝了某些數據(變量)以及對這些數據的操作(函數),從而隱藏了內部狀態,並提供了一種更安全的方式來訪問和修改數據。
```
function createCounter() {
let count = 0;
return {
increment: function() {
count++;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 输出 1
```
#### 函式工廠
函數工廠是一種強大的編程技術,使您能夠創建可複用的函數或對象,以滿足各種不同的需求和場景。它有助於提高代碼的可維護性、可擴展性和可重用性
```
function greetPrefix(prefix) {
return function(name) {
console.log(`${prefix}, ${name}`);
};
}
const greetHello = greetPrefix("Hello");
greetHello("Alice"); // 输出 "Hello, Alice"
```
### 維護狀態
閉包允許函數保持狀態,並在不同的時刻訪問和修改相同的變數,這對於維護狀態信息非常有用。例如,計數器函數可以使用閉包來記錄狀態,即每次調用時遞增計數。
```
function setupClickHandler() {
let count = 0;
document.getElementById("myButton").addEventListener("click", function() {
count++;
console.log(`Button clicked ${count} times.`);
});
}
setupClickHandler();
```
### 回調和事件處理
許多回調函數和事件處理函數都是閉包。它們可以捕獲外部範圍內的上下文信息,並在稍後的時間執行。
```
function fetchData(url, callback) {
fetch(url)
.then(response => response.json())
.then(data => {
callback(data);
});
}
let result;
fetchData("https://example.com/api/data", function(data) {
result = data;
console.log(result);
});
```
### 模塊化
通過使用閉包,您可以模擬私有變量和方法,從而創建模塊化的代碼結構,以防止全局作用域的污染。
```
const module = (function() {
let privateData = 42;
function privateFunction() {
return privateData;
}
return {
getPrivateData: privateFunction
};
})();
console.log(module.getPrivateData()); // 输出 42
```
## 商業應用
### 用戶身份驗證和授權系統
```
function createAuthenticator(strategy) {
return function (user) {
// 根據策略執行身份驗證和授權操作
return strategy.authenticate(user);
};
}
// 創建基於用戶名密碼的身份驗證策略
const usernamePasswordAuthenticator = createAuthenticator({
authenticate: function (user) {
// 執行用戶名密碼驗證邏輯
// 返回 true 或 false 表示是否通過驗證
},
});
// 創建基於社交媒體登錄的身份驗證策略
const socialMediaAuthenticator = createAuthenticator({
authenticate: function (user) {
// 執行社交媒體登錄驗證邏輯
// 返回 true 或 false 表示是否通過驗證
},
});
// 在登錄過程中使用不同的身份驗證策略
const user = { username: "user123", password: "password123" };
if (usernamePasswordAuthenticator(user)) {
// 用戶通過身份驗證
// 執行授權操作
} else {
// 用戶未通過身份驗證
// 處理身份驗證失敗
}
```
### 電商產品管理
```
function createProduct(name, price) {
return {
name: name,
price: price,
getDescription: function () {
return `Product: ${this.name}, Price: $${this.price}`;
},
};
}
// 創建不同類型的產品對象
const product1 = createProduct("Laptop", 999.99);
const product2 = createProduct("Smartphone", 599.99);
console.log(product1.getDescription()); // 輸出 "Product: Laptop, Price: $999.99"
console.log(product2.getDescription()); // 輸出 "Product: Smartphone, Price: $599.99"
```
### 在線預訂系統
```
function createReservation(customer, roomType, checkInDate, checkOutDate) {
return {
customer: customer,
roomType: roomType,
checkInDate: checkInDate,
checkOutDate: checkOutDate,
getTotalCost: function () {
// 計算預訂的總費用
// 根據客戶、房間類型和日期計算費用
},
};
}
// 创建预订对象
const reservation1 = createReservation("Alice", "Double Room", "2023-09-10", "2023-09-15");
const reservation2 = createReservation("Bob", "Single Room", "2023-09-12", "2023-09-14");
console.log(reservation1.getTotalCost()); // 輸出預訂1的總費用
console.log(reservation2.getTotalCost()); // 輸出預訂2的總費用
```