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

闭包的概念与应用

什么是闭包(Closure)

闭包 = 函数 + 它创建时所处的词法作用域。

也就是说:一个函数即使离开了它原本的作用域,仍然可以访问创建时作用域中的变量。

functionouter(){letcount=0functioninner(){count++console.log(count)}returninner}constfn=outer()fn()// 1fn()// 2fn()// 3

outer()已经执行结束了,但inner仍然能访问count,这就是闭包。


为什么count没有被销毁?

正常情况下,函数执行完成后,其局部变量可能会被回收。

但这里:

constfn=outer()

fn引用了inner函数,而inner又引用了外层变量count

引用关系大致是:

fn ↓ inner 函数 ↓ outer 的词法作用域 ↓ count

因为count仍然被使用,所以不会被垃圾回收。


闭包的核心条件

一般可以这样判断:

  1. 存在函数嵌套。
  2. 内部函数使用了外部函数的变量。
  3. 内部函数在外部函数执行结束后仍然被使用。
functionouter(){constname='小明'returnfunctioninner(){console.log(name)}}

常见用途

1. 保存状态

functioncreateCounter(){letcount=0returnfunction(){return++count}}constcounter1=createCounter()constcounter2=createCounter()console.log(counter1())// 1console.log(counter1())// 2console.log(counter2())// 1

每调用一次createCounter(),都会创建一个新的独立作用域。


2. 实现数据私有化

functioncreateWallet(){letmoney=100return{getMoney(){returnmoney},deposit(amount){money+=amount},withdraw(amount){if(amount<=money){money-=amount}}}}constwallet=createWallet()wallet.deposit(50)console.log(wallet.getMoney())// 150console.log(wallet.money)// undefined

外部不能直接访问money,只能通过暴露的方法操作。


3. 函数柯里化和参数复用

functionmultiply(a){returnfunction(b){returna*b}}constdouble=multiply(2)consttriple=multiply(3)console.log(double(5))// 10console.log(triple(5))// 15

double通过闭包记住了a = 2


4. 防抖

functiondebounce(fn,delay){lettimer=nullreturnfunction(...args){clearTimeout(timer)timer=setTimeout(()=>{fn.apply(this,args)},delay)}}

返回的函数通过闭包持续访问timer,所以能够取消上一次定时器。


经典面试题:循环中的闭包

使用var

for(vari=0;i<3;i++){setTimeout(function(){console.log(i)},0)}

输出:

3 3 3

原因是:

  • var没有块级作用域;
  • 三个回调函数共享同一个i
  • 回调执行时,循环已经结束,i变成了3

使用let

for(leti=0;i<3;i++){setTimeout(function(){console.log(i)},0)}

输出:

0 1 2

因为let在每次循环中都会创建一个新的块级绑定,每个回调闭包保存的是不同的i

使用立即执行函数解决

for(vari=0;i<3;i++){;(function(currentI){setTimeout(function(){console.log(currentI)},0)})(i)}

闭包不一定要return

很多人误以为只有返回内部函数才算闭包,其实不是。

functionouter(){constmessage='hello'setTimeout(function(){console.log(message)},1000)}outer()

传给setTimeout的回调函数使用了message,它同样形成了闭包。


闭包可能导致内存问题吗?

闭包本身不是内存泄漏,但如果不再需要的变量仍然被闭包引用,就可能无法及时回收。

functioncreateHandler(){consthugeData=newArray(1000000).fill('*')returnfunction(){console.log(hugeData.length)}}lethandler=createHandler()

只要handler仍然存在,hugeData就可能继续保留。

不需要时可以解除引用:

handler=null

现代 JavaScript 引擎也会进行作用域分析,通常只保留真正被内部函数使用的变量,而不是无条件保留所有局部变量。


闭包和作用域链的关系

constglobalValue='global'functionouter(){constouterValue='outer'returnfunctioninner(){constinnerValue='inner'console.log(innerValue)console.log(outerValue)console.log(globalValue)}}

inner查找变量时会按照作用域链向外查找:

inner 作用域 ↓ outer 作用域 ↓ 全局作用域

闭包的本质并不是“保存一个变量副本”,而是函数保留了对其词法环境的访问能力。

面试回答模板

闭包是指函数与其创建时的词法作用域的组合。当内部函数引用外部作用域中的变量,并且该函数在外部函数执行结束后仍然存在时,外部变量不会被垃圾回收。闭包常用于保存状态、数据私有化、防抖节流和函数柯里化,但如果长期持有不再需要的数据,也可能增加内存占用。

http://www.jsqmd.com/news/1166202/

相关文章:

  • 7天实战AI原生游戏:LLM NPC与神经渲染管线集成指南
  • STM32F4 DMA 双缓冲区模式实战:ADC 连续采样 1024 点零 CPU 中断处理
  • 理解力债:AI 生成代码背后被忽视的“隐形成本”
  • 2026实测视频去水印提取工具教程:电脑手机免费在线软件汇总
  • CSAPP Data Lab 13个位运算函数:从 bitXor 到 float_f2i 的完整解题思路
  • 2027地理学(及相关)考研科目/参考书目调整信息汇总
  • 2026年7月最新大连雅典官方售后服务网点地址及客服电话一览 - 亨得利官方服务中心
  • Claude 中转配置完整指南:从密钥到 settings.json
  • ClaudeCode:面向工程协作的上下文感知开发基础设施
  • 直播录屏本地管理系统:视频索引、关键帧提取与内容检索实践
  • GEO系统源码主体爱搜索GEO:基于源码的AI搜索优化自主部署全攻略 - 品牌报告
  • LTC1864与PIC24FV16KA301的高精度ADC系统设计
  • GitHub Copilot 新版计费翻车:2天消耗49美元,天价引爆开发者吐槽,国产开源 AI 迎来替代窗口期
  • LlamaIndex PDF加载实战:解决中文技术文档解析的10大陷阱
  • Skill 负责编排判断,CLI 稳定交付的执行边界
  • C# WinForm CAN 上位机开发:3步连接Kvaser卡与电机握手通信
  • Cursor 3.0实战:用自然语言开发安卓APP
  • League Akari:英雄联盟LCU API工具包实战指南
  • WK2124 SPI转4串口Linux驱动移植:RK3288平台适配3大关键步骤与排错
  • 打破Windows窗口限制:5分钟掌握SRWE终极窗口编辑技巧
  • 别再手动轮询!ChatGPT API流式响应+Server-Sent Events+前端AbortController三重稳定性保障方案
  • 【Claude Code生产力跃迁计划】:为什么顶尖团队已停用Copilot?3个不可逆的技术拐点分析
  • 合肥高心:经心尖二尖瓣瓣中瓣技术破解二次瓣膜手术困局的临床实践与学科积淀
  • ARM Cortex-M3/M4 寄存器精讲:从R0-R15到xPSR的13个关键操作场景
  • 别再为健身App的动作库头疼了!1,324个动作开源,开箱即用!
  • 单端转差分电路设计:OPA1632 全差分运放实现 0.02% THD+N 音频转换
  • Qt 6.5 QOpenGLFramebufferObject 实战:5步实现6种实时图像后处理特效
  • AI视频生成技术解析:从Diffusion模型到Even Realities实践
  • ASP.NET Core 8 JWT 实战:3步集成Swagger UI,实现Bearer Token自动认证
  • ARM64 平台内核 Crash 分析:对比 3 种工具(crash vs T32 vs QCAP)的定位效率与适用场景