# 閉包 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的總費用 ```