805 字
4 分钟
JavaScript 闭包

核心定义#

闭包是函数与其词法作用域的组合

  1. 函数可以记住并访问其声明时的作用域链
  2. 即使函数在原始作用域外执行
  3. 闭包保留对外部变量的引用(非拷贝) 这种机制允许数据封装、状态保持和模块化编程,但也可能导致内存泄漏需谨慎使用。

工作原理#

  1. 函数被创建时,保存其当前词法作用域链
  2. 当函数执行时,即使其声明作用域已销毁
  3. 仍可通过内部作用域链访问外部变量
  4. 外部变量不会被垃圾回收(因被引用)
  5. 每次外部函数调用都会创建新闭包
flowchart TD 
	A[外部函数执行] --> B[创建内部函数] 
	A --> C[声明变量 x] 
	B --> D[返回内部函数] 
	A -->|结束| E[外部作用域销毁] 
	D --> F[调用内部函数] 
	F --> G{访问 x?} 
	G -->|通过闭包| H[成功读取 x]

关键点#

  1. 词法作用域:闭包基于函数声明位置(非调用位置)
  2. 变量引用:捕获的是变量本身(非值快照)
  3. 内存保留:外部变量不会被 GC 回收
  4. 每次调用新闭包:外部函数每次调用创建独立闭包
  5. 封装性:实现私有变量(如模块模式)
  6. 性能影响:过度使用可能导致内存泄漏

常见误区#

  1. 循环闭包陷阱:在循环中创建闭包捕获索引变量(输出意外值)
  2. 内存泄漏:DOM 元素引用未释放(如事件监听器)
  3. 误认值拷贝:认为闭包保存变量初始值(实际是引用)
  4. 过度使用:所有函数都误认为闭包
  5. this 绑定混淆:闭包内 this 不继承外部函数(需箭头函数或 bind

应用场景#

场景示例闭包作用
数据封装模块模式(立即执行函数)创建私有变量和公共 API
事件处理button.addEventListener('click', (function(){...})())保留事件回调状态
函数工厂function multiplier(factor) { return x => x * factor }生成定制函数
防抖/节流debounce(func, delay)保持计时器 ID 状态
柯里化函数const add = a => b => a + b分步传递参数
异步回调setTimeout(() => console.log(x), 100)保留回调所需变量

关联知识#

  1. 作用域链:闭包实现的基础(函数保留声明时作用域)
  2. 执行上下文:包含变量对象(VO)和作用域链
  3. 垃圾回收
    • 引用计数(循环引用问题)
    • 标记清除(现代浏览器使用)
  4. 内存泄漏
    • 未移除的事件监听器
    • 意外的全局变量
    • 脱离 DOM 的引用
  5. 设计模式
    • 模块模式
    • 工厂函数
    • 单例模式
  6. this 机制:闭包内 this 默认指向全局对象(严格模式 undefined
  7. 箭头函数:无自身 this,继承外部作用域的 this

💡 黄金法则

  1. 闭包 = 函数 + 声明时的作用域链
  2. 循环内闭包用 let 或立即执行函数(IIFE)解决索引问题
  3. 移除事件监听和取消定时器防止内存泄漏 使用 Chrome DevTools 的 Memory 面板检测闭包内存占用!
JavaScript 闭包
https://website-truelovings-projects.vercel.app/posts/frontend/javascript/javascript-闭包/
作者
欢迎来到StarSky的网站!
发布于
2024-09-18
许可协议
CC BY-NC-SA 4.0