闭包 #

一、闭包概述 #

闭包(Closure)是指函数与其词法环境的组合。闭包允许内部函数访问外部函数的变量,即使外部函数已经执行完毕。

二、词法作用域 #

2.1 作用域链 #

typescript
const globalVar = "global";

function outer() {
  const outerVar = "outer";
  
  function inner() {
    const innerVar = "inner";
    
    console.log(globalVar); // global
    console.log(outerVar);  // outer
    console.log(innerVar);  // inner
  }
  
  inner();
}

outer();

2.2 词法绑定 #

函数在定义时(而非调用时)确定其作用域:

typescript
const message = "global";

function print() {
  console.log(message);
}

function execute() {
  const message = "local";
  print(); // 输出 "global",而非 "local"
}

execute();

三、闭包基础 #

3.1 基本示例 #

typescript
function createCounter() {
  let count = 0;
  
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

3.2 变量持久化 #

闭包使变量在函数执行后仍然存在:

typescript
function createGreeter(greeting: string) {
  return function(name: string) {
    return `${greeting}, ${name}!`;
  };
}

const sayHello = createGreeter("Hello");
const sayHi = createGreeter("Hi");

console.log(sayHello("Alice")); // Hello, Alice!
console.log(sayHi("Bob"));      // Hi, Bob!

3.3 多个闭包共享变量 #

typescript
function createCounter() {
  let count = 0;
  
  return {
    increment() {
      count++;
      return count;
    },
    decrement() {
      count--;
      return count;
    },
    getCount() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getCount());  // 1

四、闭包应用场景 #

4.1 数据私有化 #

typescript
function createWallet(initialBalance: number) {
  let balance = initialBalance;
  
  return {
    deposit(amount: number) {
      balance += amount;
      return balance;
    },
    withdraw(amount: number) {
      if (amount > balance) {
        throw new Error("余额不足");
      }
      balance -= amount;
      return balance;
    },
    getBalance() {
      return balance;
    }
  };
}

const wallet = createWallet(100);
console.log(wallet.deposit(50));  // 150
console.log(wallet.withdraw(30)); // 120
console.log(wallet.getBalance()); // 120
// wallet.balance = 0; // 错误:无法直接访问

4.2 函数工厂 #

typescript
function createMultiplier(factor: number) {
  return (value: number) => value * factor;
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5)); // 10
console.log(triple(5)); // 15

4.3 配置函数 #

typescript
function createConfig(defaults: Record<string, unknown>) {
  let config = { ...defaults };
  
  return {
    get(key: string) {
      return config[key];
    },
    set(key: string, value: unknown) {
      config[key] = value;
    },
    getAll() {
      return { ...config };
    },
    reset() {
      config = { ...defaults };
    }
  };
}

const config = createConfig({ host: "localhost", port: 3000 });
console.log(config.get("host")); // localhost
config.set("port", 8080);
console.log(config.getAll()); // { host: "localhost", port: 8080 }

4.4 事件处理器 #

typescript
function createButtonHandler(buttonId: string) {
  let clickCount = 0;
  
  return {
    handleClick() {
      clickCount++;
      console.log(`Button ${buttonId} clicked ${clickCount} times`);
    },
    getClickCount() {
      return clickCount;
    }
  };
}

const handler = createButtonHandler("submit");
handler.handleClick(); // Button submit clicked 1 times
handler.handleClick(); // Button submit clicked 2 times

4.5 延迟执行 #

typescript
function debounce<T extends (...args: unknown[]) => void>(
  fn: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timeoutId: number | undefined;
  
  return function(this: unknown, ...args: Parameters<T>) {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => fn.apply(this, args), delay);
  };
}

const debouncedLog = debounce(console.log, 300);
debouncedLog("hello");
debouncedLog("world"); // 只有这个会执行

4.6 节流函数 #

typescript
function throttle<T extends (...args: unknown[]) => void>(
  fn: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle = false;
  
  return function(this: unknown, ...args: Parameters<T>) {
    if (!inThrottle) {
      fn.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

const throttledLog = throttle(console.log, 1000);
throttledLog("first");  // 执行
throttledLog("second"); // 被节流

4.7 记忆化 #

typescript
function memoize<T, R>(fn: (arg: T) => R): (arg: T) => R {
  const cache = new Map<T, R>();
  
  return (arg: T) => {
    if (cache.has(arg)) {
      console.log("Cache hit");
      return cache.get(arg)!;
    }
    console.log("Cache miss");
    const result = fn(arg);
    cache.set(arg, result);
    return result;
  };
}

const expensiveCalculation = memoize((n: number): number => {
  console.log("Computing...");
  return n * n;
});

console.log(expensiveCalculation(5)); // Computing... 25
console.log(expensiveCalculation(5)); // Cache hit 25

五、循环与闭包 #

5.1 经典问题 #

typescript
// 问题:所有输出都是3
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 100);
}
// 输出:3, 3, 3

5.2 使用let解决 #

typescript
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 100);
}
// 输出:0, 1, 2

5.3 使用IIFE解决 #

typescript
for (var i = 0; i < 3; i++) {
  ((j) => {
    setTimeout(() => {
      console.log(j);
    }, 100);
  })(i);
}
// 输出:0, 1, 2

5.4 使用forEach #

typescript
[0, 1, 2].forEach((i) => {
  setTimeout(() => {
    console.log(i);
  }, 100);
});
// 输出:0, 1, 2

六、闭包与内存 #

6.1 内存占用 #

闭包会保持对外部变量的引用,可能导致内存无法释放:

typescript
function createLargeClosure() {
  const largeData = new Array(1000000).fill("data");
  
  return function() {
    console.log(largeData.length);
  };
}

const fn = createLargeClosure();
// largeData仍然存在于内存中

6.2 手动释放 #

typescript
function createHandler() {
  let largeData = new Array(1000000).fill("data");
  
  return {
    process() {
      console.log(largeData.length);
    },
    cleanup() {
      largeData = null as unknown as string[];
    }
  };
}

const handler = createHandler();
handler.process();
handler.cleanup(); // 释放内存

6.3 避免不必要的闭包 #

typescript
// 不推荐:不必要的闭包
function processItems(items: number[]) {
  return items.map((item) => {
    const process = () => item * 2; // 不必要的闭包
    return process();
  });
}

// 推荐:直接处理
function processItemsBetter(items: number[]) {
  return items.map(item => item * 2);
}

七、闭包陷阱 #

7.1 共享变量问题 #

typescript
function createFunctions() {
  const funcs: Array<() => number> = [];
  
  for (var i = 0; i < 3; i++) {
    funcs.push(() => i);
  }
  
  return funcs;
}

const funcs = createFunctions();
funcs.forEach(fn => console.log(fn())); // 3, 3, 3

7.2 this绑定问题 #

typescript
const obj = {
  value: 42,
  getValue: function() {
    return function() {
      return this.value; // this不是obj
    };
  }
};

const fn = obj.getValue();
console.log(fn()); // undefined

// 解决方案:箭头函数或bind
const obj2 = {
  value: 42,
  getValue: function() {
    return () => this.value;
  }
};

7.3 循环引用 #

typescript
function createCircular() {
  let obj: { ref?: () => void } = {};
  
  obj.ref = function() {
    console.log(obj);
  };
  
  return obj;
}

// obj和函数相互引用,可能导致内存泄漏

八、实际应用 #

8.1 模块模式 #

typescript
const module = (() => {
  let privateVar = 0;
  
  function privateMethod() {
    return privateVar;
  }
  
  return {
    increment() {
      privateVar++;
    },
    getValue() {
      return privateMethod();
    }
  };
})();

module.increment();
console.log(module.getValue()); // 1

8.2 状态管理 #

typescript
function createStore<T>(initialState: T) {
  let state = initialState;
  const listeners: Array<(state: T) => void> = [];
  
  return {
    getState() {
      return state;
    },
    setState(newState: T) {
      state = newState;
      listeners.forEach(listener => listener(state));
    },
    subscribe(listener: (state: T) => void) {
      listeners.push(listener);
      return () => {
        const index = listeners.indexOf(listener);
        listeners.splice(index, 1);
      };
    }
  };
}

const store = createStore({ count: 0 });
store.subscribe((state) => console.log("State changed:", state));
store.setState({ count: 1 }); // State changed: { count: 1 }

8.3 中间件模式 #

typescript
function createMiddleware() {
  const middlewares: Array<(ctx: unknown, next: () => void) => void> = [];
  
  return {
    use(middleware: (ctx: unknown, next: () => void) => void) {
      middlewares.push(middleware);
    },
    execute(ctx: unknown) {
      let index = 0;
      
      function next() {
        const middleware = middlewares[index++];
        if (middleware) {
          middleware(ctx, next);
        }
      }
      
      next();
    }
  };
}

const app = createMiddleware();
app.use((ctx, next) => {
  console.log("Middleware 1");
  next();
});
app.use((ctx, next) => {
  console.log("Middleware 2");
});
app.execute({});

九、总结 #

本章学习了:

  • 词法作用域
  • 闭包的基本概念
  • 闭包的应用场景
  • 循环与闭包
  • 闭包与内存
  • 闭包陷阱
  • 实际应用

下一章,我们将学习模块系统。

最后更新:2026-03-28