如何从函数执行上下文的最底层存储架构出发总结闭包应用的全部坑

胖磊吖_3943

胖磊吖_3943

2026-07-06

219人浏览

原创

闭包是未被垃圾回收的外层词法环境。坑一:var循环变量共享绑定,应用let或iife;坑二:大对象长期驻留致内存泄漏,需手动清理或用weakmap;坑三:this不被闭包捕获,应提取变量、用箭头函数或bind;坑四:异步中状态陈旧,须同步更新或运行时重读。

如何从函数执行上下文的最底层存储架构出发总结闭包应用的全部坑

闭包不是凭空出现的特性,它直接扎根于函数执行上下文(Execution Context)在 V8 引擎中的实际存储结构:词法环境(LexicalEnvironment)、变量环境(VariableEnvironment)和作用域链(Scope Chain)。当内层函数被外部持有,它所绑定的外层词法环境就无法被垃圾回收——这个“未释放的词法环境”就是闭包的物理存在形式。所有坑,都源于对这一底层内存状态的误判或忽视。

坑一:循环中创建闭包,变量引用错位

for 循环中用 var 声明计数器,再在 setTimeout 或事件回调里访问 i,结果全部输出同一个值(通常是循环终值)。根本原因在于:var 声明的 i 存在变量提升,整个循环共用一个变量环境中的 i 绑定;所有 inner 函数共享同一份词法环境引用,最终都指向最后的 i 值。

✅ 正确做法:

  • 用 let 声明循环变量 → 每次迭代创建独立的词法环境绑定
  • 用 IIFE 立即执行函数传入当前 i → 显式创建新词法环境
  • 用箭头函数 + 参数绑定(如 Array.from().map((_, i) => ...))→ 避免共享环境

坑二:闭包长期持有所致的内存泄漏

闭包让外层变量“活下来”,但如果这些变量体积大(如 DOM 节点、大数组、缓存对象),且闭包生命周期远超预期(比如全局保存、未解绑的事件监听器、未清除的定时器),就会导致内存无法释放。

✅ 关键识别与处理:

Krisp
Krisp

Krisp是一款用于会议降噪、语音增强、转写和会议记录的 AI 音频助手。

下载
  • 检查闭包返回的函数是否被意外挂到全局对象或长生命周期对象上
  • DOM 元素移除前,手动清除其关联闭包持有的引用(如 removeEventListener 对应的 handler)
  • 用 WeakMap 存储私有数据 → 键是 DOM 节点,值是闭包状态,节点销毁后自动清理
  • V8 DevTools 中 Memory 面板录制堆快照,筛选 “(closure)” 类型对象,定位异常驻留的词法环境

坑三:this 绑定与闭包环境混淆

闭包捕获的是词法环境,不是执行时的 this。在对象方法中返回一个闭包,该闭包内部的 this 仍由调用方式决定,而非外层函数的 this。常见于 React 类组件中未绑定的事件处理器、或 Promise 回调里丢失 this。

✅ 解决逻辑:

  • 避免在闭包内直接依赖外层 this —— 把需要的值提前提取为局部变量(如 const self = this;)
  • 使用箭头函数定义闭包 → 它不绑定自己的 this,而是沿词法作用域向上继承
  • 用 bind/call/apply 显式固化 this,但注意这会生成新函数,影响相等性判断

坑四:异步链中闭包状态陈旧

在多个嵌套异步操作(如 fetch → then → then)中,若闭包持续引用某个状态变量(如 loading = true),而后续操作未及时更新该变量,会导致 UI 状态与实际不符(例如 loading 一直为 true)。

✅ 核心原则:

  • 闭包中访问的状态变量,必须确保其更新路径与闭包生命周期同步
  • 优先使用不可变更新(如 setState(prev => ({ ...prev, loading: false })))代替直接赋值
  • 对需强时效性的状态(如 token、时间戳),在闭包执行时重新读取,而非依赖闭包捕获的旧值

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

javascript闭包

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

2023.06.20

4466

5

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

2023.07.04

1109

3

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

2023.07.04

4364

6

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

2023.09.01

880

4

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.04

1776

5

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

2023.09.04

3205

4

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

2023.09.12

4173

6

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

2023.09.20

2700

5

java访问控制修饰符介绍
java访问控制修饰符介绍

java访问控制修饰符有四种,分别是public、protected、private、默认访问修饰符。详细介绍:1、public,public是最宽松的访问控制修饰符,被修饰的类、方法和变量可以被任何其他类访问,当一个类、方法或变量被声明为public时,它们可以在任何地方被访问,无论是同一个包中的类还是不同包中的类;2、protected修饰符等等。

2023.09.20

868

7

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.6万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.4万人学习