閉包與進階函式模式
閉包(Closure)深入
// 閉包 = 函式 + 它能存取的外層變數
function createMultiplier(factor) {
// factor 被「關」在閉包裡
return function(number) {
return number * factor; // ← 可以存取外層的 factor
};
}
let double = createMultiplier(2); // factor = 2
let triple = createMultiplier(3); // factor = 3
console.log(double(5)); // 10
console.log(triple(5)); // 15
// 即使 createMultiplier 已經執行完畢,factor 還活在閉包裡
閉包的實用場景
私有變數
function createBankAccount(initialBalance) {
let balance = initialBalance; // ← 私有變數
return {
deposit(amount) {
if (amount > 0) balance += amount;
return balance;
},
withdraw(amount) {
if (amount > 0 && amount <= balance) balance -= amount;
return balance;
},
getBalance() {
return balance;
}
};
}
let account = createBankAccount(1000);
console.log(account.getBalance()); // 1000
account.deposit(500);
console.log(account.getBalance()); // 1500
// account.balance ← undefined(無法直接存取!)
記憶化(Memoization)
function memoize(fn) {
let cache = {}; // ← 快取結果
return function(...args) {
let key = JSON.stringify(args);
if (cache[key] !== undefined) {
console.log("從快取取得");
return cache[key];
}
let result = fn(...args);
cache[key] = result;
return result;
};
}
// 使用
let expensiveCalc = memoize((n) => {
console.log("計算中...");
return n * n * n;
});
expensiveCalc(5); // "計算中..." → 125
expensiveCalc(5); // "從快取取得" → 125(不重算!)
柯里化(Currying)
// 把多參數函式轉成一系列單參數函式
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn(...args);
}
return (...moreArgs) => curried(...args, ...moreArgs);
};
}
// 使用
function add(a, b, c) {
return a + b + c;
}
let curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
// 實用:建立特化版本
let addTax = curriedAdd(1.08); // 固定第一個參數
let addTaxAndShipping = addTax(50); // 固定第二個參數
組合(Composition)
// 把多個函式串起來
function compose(...fns) {
return (value) => fns.reduceRight((acc, fn) => fn(acc), value);
}
function pipe(...fns) {
return (value) => fns.reduce((acc, fn) => fn(acc), value);
}
// 使用
let trim = s => s.trim();
let lower = s => s.toLowerCase();
let addExclamation = s => s + "!";
// compose 從右到左:addExclamation(lower(trim(" Hello ")))
let process1 = compose(addExclamation, lower, trim);
console.log(process1(" Hello ")); // "hello!"
// pipe 從左到右(更直觀)
let process2 = pipe(trim, lower, addExclamation);
console.log(process2(" Hello ")); // "hello!"
IIFE(立即執行函式)
// 定義後立刻執行,常用來建立獨立作用域
(function() {
let secret = "私有變數";
console.log(secret); // ← 可以存取
})();
console.log(secret); // ← ❌ Error(外面存取不到)
// 現代寫法:用區塊 + let/const 取代
{
let secret = "私有變數";
console.log(secret);
}