找回密码
 立即注册
首页 业界区 安全 Typescript中闭包的原理

Typescript中闭包的原理

吕梓美 2025-10-1 17:40:02
在 TypeScript(以及 JavaScript)中,闭包描述了函数能够访问其声明时所在作用域的变量,即使该函数在其声明的作用域之外被调用的现象。
定义:闭包是指一个函数能够记住并访问其词法作用域(lexical scope)中的变量,即使这个函数是在其词法作用域之外执行。
闭包的核心原理


  • 作用域链机制:

    • 每个函数在创建时都会形成一个作用域
    • 当函数内部访问变量时,会先在自身作用域查找,如果找不到则向上级作用域查找,直到全局作用域
    • 这种层级关系形成了作用域链

  • 函数与作用域的绑定:

    • 闭包的本质是函数与其声明时所处的词法环境(lexical environment)的组合
    • 当函数被创建时,会捕获(close over)其周围的变量和函数,形成一个封闭的环境

  • 延长变量生命周期:

    • 正常情况下,函数执行完毕后,其内部变量会被垃圾回收
    • 但如果存在闭包引用了这些变量,它们会被保留下来,直到闭包不再被引用

TypeScript 中的闭包示例
  1. function outerFunction(message: string) {
  2.   // 这个变量会被内部函数捕获
  3.   const count = 0;
  4.   
  5.   // 内部函数形成闭包
  6.   function innerFunction() {
  7.     // 可以访问outerFunction的变量
  8.     console.log(message);
  9.     console.log(count);
  10.   }
  11.   
  12.   // 返回闭包
  13.   return innerFunction;
  14. }
  15. // 接收闭包
  16. const closure = outerFunction("Hello, Closure!");
  17. // 即使outerFunction已执行完毕,仍能访问其内部变量
  18. closure(); // 输出: "Hello, Closure!" 和 0
复制代码
 
闭包的用途


  • 数据私有化:创建私有变量和方法
  • 函数工厂:根据参数生成不同功能的函数
  • 回调函数:在异步操作中保持状态
  • 模块模式:实现模块化代码
TypeScript 对闭包的增强

TypeScript 作为 JavaScript 的超集,完全支持闭包特性,并且通过类型系统增强了闭包的使用:
  1. function createCounter(initial: number): () => number {
  2.   let count = initial;
  3.   
  4.   return function(): number {
  5.     return count++;
  6.   };
  7. }
  8. // 类型推断会正确识别返回的函数类型
  9. const counter = createCounter(10);
  10. console.log(counter()); // 10
  11. console.log(counter()); // 11
复制代码
实例

让我们看一个用 TypeScript 编写的经典例子:
  1. function outerFunction(outerValue: number) {
  2.   // 外部函数的变量
  3.   const outerString: string = "Hello";
  4.   // 内部函数 -> 闭包开始形成
  5.   function innerFunction(innerValue: number): void {
  6.     console.log(`Outer value: ${outerValue}`);
  7.     console.log(`Outer string: ${outerString}`);
  8.     console.log(`Inner value: ${innerValue}`);
  9.     console.log(`Total: ${outerValue + innerValue}`);
  10.   }
  11.   // 返回内部函数,使其能够在 outerFunction 的作用域外被执行
  12.   return innerFunction;
  13. }
  14. // 调用 outerFunction,它返回 innerFunction
  15. // 此时,closure 就是一个闭包实例,它“关闭”了 outerFunction 的作用域
  16. const closure = outerFunction(10);
  17. // 在 outerFunction 的执行上下文已经销毁后,调用 closure
  18. // 但是 closure 仍然能访问到 outerValue 和 outerString
  19. closure(5); // 输出:
  20. // Outer value: 10
  21. // Outer string: Hello
  22. // Inner value: 5
  23. // Total: 15
复制代码
 
原理深入剖析:执行上下文与词法环境

在底层,JavaScript 引擎通过执行上下文和词法环境来实现闭包。

  • 调用 outerFunction(10):

    • 创建一个新的执行上下文,并将其推入调用栈。
    • 创建对应的词法环境,其中包含了参数 outerValue 和局部变量 outerString。
    • 定义 innerFunction。此时,innerFunction 的内部属性 [[Environment]] 会被设置为当前(即 outerFunction 的)词法环境的引用。这一步至关重要,它建立了作用域链的连接。

  • outerFunction 执行完毕:

    • 其执行上下文从调用栈中弹出。
    • 但是,因为返回的 innerFunction 的 [[Environment]] 仍然引用着 outerFunction 的词法环境,所以这个词法环境不会被垃圾回收机制销毁。它被保留在内存中,以便 innerFunction 将来使用。

  • 调用 closure(5) (也就是 innerFunction(5)):

    • 为 innerFunction 创建一个新的执行上下文和词法环境。
    • 这个新词法环境的外部环境引用指向的就是之前在 [[Environment]] 中保存的那个 outerFunction 的词法环境。
    • 当 innerFunction 尝试访问 outerValue 或 outerString 时,引擎会沿着这条作用域链查找,成功在 outerFunction 的词法环境中找到它们。

 理解闭包有助于编写更优雅、更模块化的代码,但也要注意过度使用可能导致的内存问题,因为被闭包引用的变量不会被垃圾回收机制回收。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册