【TypeScript】闭包

文章目录

  • 一、作用域介绍
    • [1、全局作用域(Global Scope)](#1、全局作用域(Global Scope))
    • [2、函数作用域(Function Scope)](#2、函数作用域(Function Scope))
    • [3、块级作用域(Block Scope)](#3、块级作用域(Block Scope))
    • [4、模块作用域(Module Scope)](#4、模块作用域(Module Scope))
    • [5、类作用域(Class Scope)](#5、类作用域(Class Scope))
    • [6、词法作用域(Lexical Scope,也称为静态作用域)](#6、词法作用域(Lexical Scope,也称为静态作用域))
  • 二、闭包
    • 1、定义
    • 2、特性
    • 3、作用
      • [3.1 数据私有化](#3.1 数据私有化)
      • [3.2 记忆函数](#3.2 记忆函数)
      • [3.3 闭包与箭头函数](#3.3 闭包与箭头函数)
      • [3.4 闭包与作用域链](#3.4 闭包与作用域链)

一、作用域介绍

1、全局作用域(Global Scope)

全局作用域是程序中最外层的作用域,在全局作用域中声明的标识符(变量、函数、类等)在整个程序的任何地方都可访问。

  • 特点:
    • 声明在函数、类、模块之外的标识符属于全局作用域。
JavaScript 复制代码
// 全局变量
const globalVar: string = "全局变量";

// 全局函数
function globalFunc(): void {
  console.log(globalVar); // 可访问全局变量
}

class GlobalClass {
  // 全局类
}

// 任何地方都可访问
console.log(globalVar);
globalFunc();
const obj = new GlobalClass();

2、函数作用域(Function Scope)

函数作用域指在函数内部声明的标识符,仅在该函数内部可访问,外部无法直接访问。

  • 特点:
    • 函数的参数、内部变量、内部函数均属于函数作用域。
    • 函数作用域内的标识符会 "遮蔽"(shadow)外部作用域的同名标识符。
TypeScript 复制代码
const outerVar: string = "外部变量";

function funcScope() {
  // 函数内部变量(仅在funcScope内可访问)
  const innerVar: number = 10;
  
  // 函数参数(属于函数作用域)
  console.log(innerVar); // 正确:10
  
  // 遮蔽外部同名变量
  const outerVar: string = "内部变量";
  console.log(outerVar); // 正确:"内部变量"
}

funcScope();
console.log(innerVar); // 错误:innerVar未定义(函数外部不可访问)

3、块级作用域(Block Scope)

块级作用域由{}(大括号)界定,如ifforwhileswitch语句或单独的代码块。letconst声明的变量属于块级作用域,var声明的变量不具备块级作用域(仅函数作用域)。

TypeScript 复制代码
if (true) {
  var name = "Bob"; // 函数作用域(此处无函数,实际为全局变量)
  console.log(name); // 正常输出:Bob
}
console.log(name); // 正常输出:Bob(块外可访问,因 var 无块级作用域)
  • 特点:
    • 块级作用域内的变量仅在当前块及嵌套块中可访问。
    • 支持 "块级遮蔽",即内部块的变量可遮蔽外部块的同名变量。
TypeScript 复制代码
// 块级作用域示例(if语句块)
if (true) {
  let blockVar: string = "块内变量"; // let声明的块级变量
  const blockConst: number = 20; // const声明的块级变量
  console.log(blockVar); // 正确:"块内变量"
}

console.log(blockVar); // 错误:blockVar未定义(块外部不可访问)

// var声明无块级作用域(仅函数作用域)
for (var i = 0; i < 3; i++) {}
console.log(i); // 正确:3(var变量泄露到外部作用域)

// 块级遮蔽
const x: number = 100;
{
  const x: number = 200; // 遮蔽外部x
  console.log(x); // 正确:200
}
console.log(x); // 正确:100

4、模块作用域(Module Scope)

在 TS 中,一个文件默认就是一个模块(Module),模块内部的顶层声明(变量、函数、类等)属于模块作用域,仅在模块内部可访问,外部需通过export导出后才能访问。

  • 特点:
    • 模块作用域隔离了不同文件的代码,避免全局污染。
    • 模块内的声明需显式export才能被其他模块import
JavaScript 复制代码
// moduleA.ts(模块文件)
const moduleVar: string = "模块内变量"; // 模块作用域变量

export function moduleFunc(): void {
  console.log(moduleVar); // 模块内可访问
}

// 其他文件中使用
import { moduleFunc } from './moduleA';
moduleFunc(); // 正确:可访问导出的函数
console.log(moduleVar); // 错误:moduleVar未导出,不可访问

5、类作用域(Class Scope)

类作用域指类内部的成员(属性、方法)的可访问范围,受访问修饰符(publicprivateprotected)控制。

  • 特点:
    • public:默认修饰符,类内部、实例、子类均可访问。
    • private:仅类内部可访问,实例和子类不可访问。
    • protected:类内部和子类可访问,实例不可访问。
TypeScript 复制代码
class ClassScope {
  public publicProp: string = "公共属性"; // 类作用域(public)
  private privateProp: number = 100; // 类作用域(private)
  protected protectedProp: boolean = true; // 类作用域(protected)

  public getPrivate(): number {
    return this.privateProp; // 类内部可访问private成员
  }
}

class SubClass extends ClassScope {
  getProtected(): boolean {
    return this.protectedProp; // 子类可访问protected成员
  }
}

const instance = new ClassScope();
console.log(instance.publicProp); // 正确:public成员可访问
console.log(instance.privateProp); // 错误:private成员不可访问(实例)
console.log(instance.protectedProp); // 错误:protected成员不可访问(实例)

6、词法作用域(Lexical Scope,也称为静态作用域)

是指变量的可访问范围由其声明时的位置决定,而非运行时的执行位置。这是 JavaScript/TypeScript 等语言遵循的作用域规则,也是理解变量访问权限的核心概念。

  • 特点:

    • 静态确定性:作用域在代码 "定义阶段" 就已固定,与函数 / 变量的 "调用位置" 无关。

    TypeScript 复制代码
    const a = 10;
    function outer() {
      const b = 20;
      function inner() {
        console.log(b); // inner定义时处于outer作用域内,因此能访问b
      }
      return inner;
    }
    const func = outer();
    func(); // 输出20(即使在全局调用,仍访问outer中的b)
    • 嵌套作用域链:作用域按代码的嵌套关系形成 "由内向外" 的链条(作用域链)。访问变量时,引擎会先在当前作用域查找,若未找到则逐层向上搜索外层作用域,直到全局作用域。

    JavaScript 复制代码
    const global = "全局";
    function f1() {
      const f1Var = "f1";
      function f2() {
        const f2Var = "f2";
        console.log(f2Var); // 当前作用域
        console.log(f1Var); // 外层f1作用域
        console.log(global); // 全局作用域
      }
      f2();
    }
    f1();

二、闭包

在 TypeScript 中,闭包(Closure) 是一个核心概念,它允许函数访问并记住其词法****作用域内的变量,即使该函数在其原始作用域之外执行。

1、定义

闭包是指有权访问另一个函数作用域中变量的函数。即使该外部函数已执行完毕,其作用域内的变量也不会被销毁,而是会被闭包 "捕获" 并保留引用。

2、特性

  1. 捕获词法环境:闭包会记住其定义时所在的词法环境(即外部函数的作用域),包括该作用域内的所有变量。
  2. 延长变量生命周期:即使外部函数执行完毕,其作用域内的变量也不会被垃圾回收,因为闭包仍然引用它们。
  3. 动态访问变量 :闭包捕获的是变量的引用,而非值的副本,因此变量的变化会反映在闭包中。
JavaScript 复制代码
function outer() {
    //outer 函数内部创建了变量 count 和函数 inner
  let count = 0;

  function inner() {
    count++;
    console.log(count);
  }

  return inner;
}

const closure = outer();
closure(); // 输出: 1
closure(); // 输出: 2
  • 词法****作用域inner 函数在定义时(即在 outer 内部),其作用域链包含 outer 的变量(如 count)。
  • 闭包的形成 :当 inner 函数被返回并在其他地方调用时,它携带了整个词法环境 (即 outer 的作用域),即使 outer 已执行完毕。

在上述代码中,inner 函数形成了一个闭包。inner 函数可以访问 outer 函数作用域内的 count 变量,即使 outer 函数已经执行完毕,count 变量的值仍然被 inner 函数记住。

3、作用

3.1 数据私有化

闭包可以用来实现数据的私有化,外部无法直接访问闭包内部的变量。

JavaScript 复制代码
 function createCounter() {
   let value = 0;
 
   return {
     increment() {
       value++;
     },
     getValue() {
       return value;
     }
   };
 }
 
 const counter = createCounter();
 counter.increment();
 console.log(counter.getValue()); // 输出: 1

在这个例子中,value 变量被封装在闭包中,外部只能通过返回的对象方法来操作 value,实现了数据的私有化。

3.2 记忆函数

闭包可以用来创建记忆函数,记住之前的计算结果。

TypeScript 复制代码
function memoize(func: (n: number) => number): (n: number) => number {
    const cache = new Map<number, number>(); // 用于存储已计算的结果
    return function(arg: number): number { // 返回一个闭包函数
        if (cache.has(arg)) { // 如果缓存中已有结果
            return cache.get(arg) as number; // 直接从缓存中返回
        }
        // 否则计算结果并缓存
        const result: number = func(arg);
        cache.set(arg, result);
        return result;
    };
}

function factorial(n: number): number {
    return n === 0 ? 1 : n * factorial(n - 1);
}

const memoizedFactorial = memoize(factorial);
console.log(memoizedFactorial(5));
console.log(memoizedFactorial(5)); // 从缓存中获取结果,提高性能

memoize 函数返回的新函数形成了闭包,记住了之前的计算结果,避免了重复计算。

3.3 闭包与箭头函数

箭头函数也可以形成闭包,但箭头函数本身不创建自己的 this,它捕获其定义时所在作用域的 this

TypeScript 复制代码
class Example {
  private value = 42;

  getValue = () => {
    setTimeout(() => {
      console.log(this.value); // 箭头函数捕获了 Example 实例的 this
    }, 1000);
  };
}

const instance = new Example();
instance.getValue(); // 输出: 42

在这个例子中,getValue 箭头函数形成了闭包,并且捕获了 Example 实例的 this,即使在 setTimeout 回调中也能正确访问 value

3.4 闭包与作用域链

闭包依赖于作用域链,当函数访问一个变量时,会先在自己的作用域中查找,如果找不到则沿着作用域链向上查找。

JavaScript 复制代码
function outer() {
  let outerVar = "外部变量";

  function inner() {
    let innerVar = "内部变量";
    console.log(innerVar); // 找到内部变量
    console.log(outerVar); // 沿着作用域链找到外部变量
  }

  return inner;
}

const closure = outer();
closure();

在这个例子中,inner 函数可以访问其自身作用域内的 innerVar 和外部作用域的 outerVar,这是通过作用域链实现的。

相关推荐
葡萄城技术团队9 小时前
TypeScript 队列实战:从零实现简单、循环、双端、优先队列,附完整测试代码
typescript
烛阴1 天前
【TS 设计模式完全指南】从“入门”到“劝退”,彻底搞懂单例模式
javascript·设计模式·typescript
lypzcgf1 天前
Coze源码分析-资源库-删除插件-前端源码-核心组件实现
前端·typescript·前端框架·react·coze·coze插件·智能体平台
江拥羡橙1 天前
【目录-单选】鸿蒙HarmonyOS开发者基础
前端·ui·华为·typescript·harmonyos
叫我阿柒啊1 天前
从Java全栈到Vue3实战:一次真实面试中的技术探索
java·数据库·spring boot·微服务·typescript·vue3·restful
江拥羡橙2 天前
【目录-多选】鸿蒙HarmonyOS开发者基础
前端·ui·华为·typescript·harmonyos
叫我阿柒啊2 天前
从Java全栈到前端框架:一次真实的面试对话与技术解析
java·javascript·typescript·vue·springboot·react·前端开发
lypzcgf2 天前
Coze源码分析-资源库-删除提示词-前端源码
前端·typescript·react·ai应用·coze·coze源码分析·智能体平台
子兮曰2 天前
🚀99% 的前端把 reduce 用成了「高级 for 循环」—— 这 20 个骚操作让你一次看懂真正的「函数式折叠」
前端·javascript·typescript