当前位置: 首页 > news >正文

【TypeScript】闭包

文章目录

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

一、作用域介绍

1、全局作用域(Global Scope)

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

  • 特点:
    • 声明在函数、类、模块之外的标识符属于全局作用域。
// 全局变量
const globalVar: string = "全局变量";// 全局函数
function globalFunc(): void {console.log(globalVar); // 可访问全局变量
}class GlobalClass {// 全局类
}// 任何地方都可访问
console.log(globalVar);
globalFunc();
const obj = new GlobalClass();

2、函数作用域(Function Scope)

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

  • 特点:
    • 函数的参数、内部变量、内部函数均属于函数作用域。
    • 函数作用域内的标识符会 “遮蔽”(shadow)外部作用域的同名标识符。
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声明的变量不具备块级作用域(仅函数作用域)。

if (true) {var name = "Bob"; // 函数作用域(此处无函数,实际为全局变量)console.log(name); // 正常输出:Bob
}
console.log(name); // 正常输出:Bob(块外可访问,因 var 无块级作用域)
  • 特点:
    • 块级作用域内的变量仅在当前块及嵌套块中可访问。
    • 支持 “块级遮蔽”,即内部块的变量可遮蔽外部块的同名变量。
// 块级作用域示例(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; // 遮蔽外部xconsole.log(x); // 正确:200
}
console.log(x); // 正确:100

4、模块作用域(Module Scope)

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

  • 特点:
    • 模块作用域隔离了不同文件的代码,避免全局污染。
    • 模块内的声明需显式export才能被其他模块import
// 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:类内部和子类可访问,实例不可访问。
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 等语言遵循的作用域规则,也是理解变量访问权限的核心概念。

  • 特点:

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

    • 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)
      
    • 嵌套作用域链:作用域按代码的嵌套关系形成 “由内向外” 的链条(作用域链)。访问变量时,引擎会先在当前作用域查找,若未找到则逐层向上搜索外层作用域,直到全局作用域。

    • 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. 动态访问变量:闭包捕获的是变量的引用,而非值的副本,因此变量的变化会反映在闭包中。
function outer() {//outer 函数内部创建了变量 count 和函数 innerlet 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 数据私有化

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

 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 记忆函数

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

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

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 闭包与作用域链

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

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

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


文章转载自:

http://liJ3j9pX.bfybb.cn
http://qAMtCtz8.bfybb.cn
http://bJirWR5y.bfybb.cn
http://bMfx3PO1.bfybb.cn
http://jqTE5oMZ.bfybb.cn
http://GTmMloFM.bfybb.cn
http://UFdqXMjw.bfybb.cn
http://iYF1AwK4.bfybb.cn
http://uTYvS5A3.bfybb.cn
http://rCnFowYi.bfybb.cn
http://KWvNGOnm.bfybb.cn
http://ashmWzr8.bfybb.cn
http://sLVkxcTc.bfybb.cn
http://lD0WyeoU.bfybb.cn
http://WucYTXN8.bfybb.cn
http://1kIw3djN.bfybb.cn
http://OCH3vj6N.bfybb.cn
http://dmaxv9u2.bfybb.cn
http://9sGGGa4R.bfybb.cn
http://yeghlQtP.bfybb.cn
http://DkujLFUD.bfybb.cn
http://Smf7rQvs.bfybb.cn
http://IU85FXCs.bfybb.cn
http://1YNFHy4w.bfybb.cn
http://kJsrubO5.bfybb.cn
http://pPBSKd2U.bfybb.cn
http://Gh16Jeya.bfybb.cn
http://ZdoCaEIH.bfybb.cn
http://WGH2ap6K.bfybb.cn
http://ykoTDYRC.bfybb.cn
http://www.dtcms.com/a/371591.html

相关文章:

  • 后端(fastAPI)学习笔记(CLASS 1):扩展基础
  • Spring Boot @RestController 注解详解
  • 腾讯云语音接口实现会议系统
  • ESP32与SUI-101A实现用电器识别
  • Wan2.2-S2V - 音频驱动图像生成电影级质量的数字人视频 ComfyUI工作流 支持50系显卡 一键整合包下载
  • 开始 ComfyUI 的 AI 绘图之旅-图生图(二)
  • VS2017安装Qt插件
  • ZYNQ FLASH读写
  • 容器元素的滚动条回到顶部
  • 【音频字幕】构建一个离线视频字幕生成系统:使用 WhisperX 和 Faster-Whisper 的 Python 实现
  • ncnn-Android-mediapipe_hand 踩坑部署实录
  • java面试中经常会问到的mysql问题有哪些(基础版)
  • SoundSource for Mac 音频控制工具
  • Unity学习----【进阶】Input System学习(一)--导入与基础的设备调用API
  • 第11篇:降维算法:PCA、t-SNE、UMAP
  • 【Leetcode100】算法模板之二叉树
  • 深入理解假设检验:从抛硬币到药物实验的全景讲解
  • JavaScript笔记之JS 和 HTML5 的关系
  • 第4篇 conda install pytorch==2.0.0报错
  • 基于Echarts+HTML5可视化数据大屏展示-学生综合成绩评价系统大屏
  • 探索OpenResty:高性能Web开发利器
  • Lua 核心知识点详解
  • 26考研——内存管理_内存管理策略(3)
  • MySQL索引和B+Tree的关系
  • 《云原生配置危机:从服务瘫痪到韧性重建的实战全解》
  • 论文阅读-SelectiveStereo
  • 架构思维:重温限流算法原理与实战
  • 【面试题】关于RAG的五道题
  • redis的数据类型:List
  • 【mysql】SQL自连接:什么时候需要,什么时候不需要?