news 2026/4/18 13:15:17

01_闭包原理:从变量作用域到闭包的 3 个实际用途(以计数器为例)【包含课程视频】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
01_闭包原理:从变量作用域到闭包的 3 个实际用途(以计数器为例)【包含课程视频】

闭包原理:从变量作用域到闭包的 3 个实际用途(以计数器为例)

点击这里观看课程

一、前置知识:理解变量作用域(闭包的基础)

要理解闭包,首先要搞清楚变量作用域——变量能被访问的范围,主要分为两种:

1. 全局作用域

  • 定义在函数外部的变量,整个程序都能访问和修改,容易造成变量污染。
  • 示例:
    letcount=0;// 全局变量functionadd(){count++;console.log(count);}add();// 输出 1add();// 输出 2count=100;// 全局变量可被随意修改,导致计数器逻辑混乱add();// 输出 101(不符合计数器预期)

2. 函数作用域(局部作用域)

  • 定义在函数内部的变量,只有函数内部能访问,外部无法直接修改,更安全,但函数执行后变量会被销毁。
  • 示例:
    functionadd(){letcount=0;// 局部变量count++;console.log(count);}add();// 输出 1(函数执行后 count 被销毁)add();// 输出 1(重新创建 count 并赋值 0)

问题:全局变量不安全,局部变量无法保留状态——闭包就是解决这个矛盾的核心方案。

二、闭包的核心原理

1. 闭包的定义

闭包是指内层函数可以访问外层函数作用域中的变量,即使外层函数已经执行完毕。简单来说:

  • 外层函数包裹内层函数;
  • 内层函数引用外层函数的局部变量;
  • 外层函数返回内层函数。

此时,外层函数的作用域不会被销毁,变量会被“保留”,且外部无法直接修改,只能通过内层函数操作。

2. 闭包的核心逻辑(以计数器为例)

// 外层函数:创建计数器的“容器”functioncreateCounter(){letcount=0;// 外层函数的局部变量,被内层函数引用// 内层函数:操作局部变量的逻辑functionincrement(){count++;returncount;}returnincrement;// 返回内层函数,形成闭包}// 使用闭包创建计数器constcounter=createCounter();console.log(counter());// 输出 1(count 保留了上一次的状态)console.log(counter());// 输出 2console.log(counter());// 输出 3// 无法直接修改 count,保证数据安全console.log(count);// 报错:count is not defined

原理拆解

  1. 执行createCounter()时,创建局部变量count和函数increment
  2. 返回increment函数并赋值给counter,此时createCounter执行完毕,但因为increment引用了count,所以count不会被垃圾回收;
  3. 每次调用counter(),都会访问并修改同一个count,实现状态保留。

三、闭包的 3 个实际用途

用途 1:保留函数状态(核心用途,如计数器/累加器)

除了基础计数器,还可以扩展为带初始值的计数器,适配更多场景:

functioncreateCounter(initialValue=0){letcount=initialValue;return{increment:()=>{count++;returncount;},decrement:()=>{count--;returncount;},reset:()=>{count=initialValue;returncount;},getCount:()=>count// 只读访问,避免直接修改};}// 示例:创建初始值为 10 的计数器constmyCounter=createCounter(10);console.log(myCounter.increment());// 11console.log(myCounter.decrement());// 10console.log(myCounter.reset());// 10console.log(myCounter.getCount());// 10

用途 2:封装私有变量(模拟“私有属性/方法”)

JavaScript 原生没有private关键字,闭包可以实现变量私有化,只暴露指定的操作方法:

functioncreatePerson(name){// 私有变量:外部无法直接访问letage=0;// 暴露的公共方法return{getName:()=>name,growUp:()=>{age++;returnage;},getAge:()=>age};}constperson=createPerson("小明");console.log(person.getName());// 输出 小明console.log(person.growUp());// 输出 1console.log(person.age);// 输出 undefined(私有变量无法直接访问)

用途 3:防抖/节流(前端高频场景,利用闭包保留状态)

闭包可以保留防抖/节流函数中的定时器、上次执行时间等状态,避免全局变量污染:

// 防抖函数:频繁触发时,只执行最后一次functiondebounce(fn,delay){lettimer=null;// 闭包保留定时器状态returnfunction(...args){clearTimeout(timer);// 清除上一次的定时器timer=setTimeout(()=>{fn.apply(this,args);},delay);};}// 用法:监听窗口resize,避免频繁触发window.addEventListener("resize",debounce(()=>{console.log("窗口大小改变了");},500));

四、闭包的注意事项

  1. 内存泄漏风险:闭包会保留外层作用域的变量,若长期不释放(如全局变量引用闭包),可能导致内存占用过高。解决:不再使用时,手动将闭包引用赋值为null(如counter = null)。
  2. 性能问题:闭包的作用域链查找比普通变量慢,高频执行的代码需谨慎使用。
  3. 避免滥用:不是所有场景都需要闭包,简单的状态管理可优先用其他方式(如 React 的 useState、Vue 的 ref)。

总结

  1. 闭包核心:内层函数引用外层函数的局部变量,使外层变量在函数执行后不被销毁,实现状态保留和私有化;
  2. 核心用途:保留函数状态(计数器)、封装私有变量、实现防抖/节流等高频前端功能;
  3. 使用注意:避免内存泄漏,不滥用闭包,根据场景选择最优方案。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 3:36:54

【开题答辩全过程】以 基于微信小程序的咖啡销售系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人,语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/4/18 3:26:50

【开题答辩全过程】以 基于SSM的航班管理系统的设计与实现为例,包含答辩的问题和答案

个人简介一名14年经验的资深毕设内行人,语言擅长Java、php、微信小程序、Python、Golang、安卓Android等开发项目包括大数据、深度学习、网站、小程序、安卓、算法。平常会做一些项目定制化开发、代码讲解、答辩教学、文档编写、也懂一些降重方面的技巧。感谢大家的…

作者头像 李华
网站建设 2026/4/18 3:35:48

多 GitHub 账号与多平台 Git 使用(附加场景:就想用指定账号clone)

前一篇【多 GitHub 账号与多平台 Git 使用】解释了标准流程,有时我就是这么非常的想用指定账号clone,还就想在一个不一样的目录,这是就有了下边的问题: ----------------------------------------------- 如果我临时clone一些gi…

作者头像 李华
网站建设 2026/4/18 3:31:17

专精特新培育:从政策红利到能力红利的系统跃迁之路

专精特新培育:从政策红利到能力红利的系统跃迁之路在中国制造业转型升级的关键时期,“专精特新”已成为中小企业高质量发展的明确路径。然而,真正的专精特新培育,绝非简单的资质申报或政策对接,而是一场深刻的系统性能…

作者头像 李华
网站建设 2026/4/17 13:03:43

MongoDB实现发布订阅机制

一、MongoDB Pub/Sub 的实现原理 MongoDB 的发布订阅不是像 Redis 那样的原生 “频道式” Pub/Sub,而是基于变更流(Change Streams)(MongoDB 3.6 推荐)或早期的tailable cursor(可尾游标)实现&a…

作者头像 李华
网站建设 2026/4/18 3:33:09

特殊符号绕过-ctfshow-web40

一、打开环境看源码if(isset($_GET[c])){$c $_GET[c];if(!preg_match("/[0-9]|\~|\|\|\#|\\$|\%|\^|\&|\*|\&#xff08;|\&#xff09;|\-|\|\|\{|\[|\]|\}|\:|\|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){eval($c);}}else{highlight_file(__FILE__); }…

作者头像 李华