JavaScript的闭包是什么_它有什么实际用途【教程】
发布时间 - 2026-02-02 00:00:00 点击率:次闭包是JavaScript中函数作用域与词法环境自然结合的必然结果;其形成依赖函数的[[Environment]]属性和自由变量查找机制,用于封装私有状态、保持异步上下文,并需警惕内存泄漏。
闭包不是语法糖,也不是高级技巧——它是 JavaScript 中函数作用域和词法环境自然结合的必然结果。只要一个函数在定义它的词法作用域之外被调用,且它引用了该作用域中的变量,闭包就产生了。
闭包是怎么形成的:看懂 [[Environment]] 和自由变量
每个函数对象内部都有一个隐藏属性 [[Environment]],它指向函数定义时所在的作用域链。当函数执行时,如果访问了自己作用域中未声明的变量(即“自由变量”),引擎就会顺着 [[Environment]] 一层层向上查找——这个被保留下来的外层词法环境,就是闭包的核心。
常见错误现象:
- 循环中用
var声明变量 + 异步回调,所有回调都输出最后一个值(因为共享同一个变量绑定) - 误以为
setTimeout的回调“捕获了当前值”,其实捕获的是变量引用
正确做法是让每次迭代拥有独立作用域:
for (var i = 0; i < 3; i++) {
(function(i) {
setTimeout(() => console.log(i), 100);
})(i);
}
// 或更现代的写法:
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
let 每次迭代都会创建新绑定,[[Environment]] 指向各自独立的块级环境,本质仍是闭包。
封装私有状态:用闭包替代 class 私有字段(尤其兼容旧环境)
ES6

class 的 #private 字段在 IE 和部分老版本 Node.js 中不可用,而闭包在任何支持函数的一版 JS 中都有效。
使用场景:
- 模块导出 API 时隐藏实现细节(如缓存、计数器、配置)
- 避免全局污染或意外修改关键状态
- 需要精细控制 getter/setter 行为(比如带校验的 setter)
示例:
function createCounter() {
let count = 0; // 外部无法直接访问
return {
increment() { count++; },
get value() { return count; },
reset() { count = 0; }
};
}
const counter = createCounter();
counter.increment();
console.log(counter.value); // 1
console.log(counter.count); // undefined —— 真正私有
注意:这里返回的对象方法都闭包了 count,但对象本身没有暴露该变量。
事件监听与定时器中保持上下文:为什么 this 不是唯一问题
很多人只记得用 .bind() 或箭头函数解决 this 绑定,却忽略另一个更隐蔽的问题:依赖的局部变量可能已变更或销毁。
典型场景:
- 组件卸载后,异步回调仍尝试更新已销毁的 DOM 或 React state
- 轮询请求中,用户切换页面,旧的
id或token仍被后续响应使用
闭包能帮你“冻结”那一刻所需的最小数据集:
function startPolling(id, token) {
const poll = () => {
fetch(`/api/data?id=${id}`, {
headers: { Authorization: `Bearer ${token}` }
}).then(r => r.json())
.then(data => updateUI(data));
};
const timer = setInterval(poll, 5000);
// 返回清理函数,利用闭包捕获 timer 和 id
return () => clearInterval(timer);
}
const stop = startPolling(123, 'abc');
// 后续可安全调用 stop(),无需再传参数
这里 stop 函数闭包了 timer,而不是靠外部变量管理——这是健壮性的关键。
闭包真正的复杂点不在“怎么写”,而在“什么时候不该用”:过度闭包会阻止垃圾回收,导致内存泄漏;在频繁创建函数的循环里(如渲染大量列表项),每个闭包都携带一份环境引用,开销比想象中大。判断依据很简单——只闭包真正需要的数据,其余尽量通过参数传入。
# react
# javascript
# es6
# java
# js
# node.js
# json
# node
# 作用域
# 为什么
# count
# 封装
# Token
# 局部变量
# 循环
# class
# private
# var
# 闭包
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
HTML5空格和margin有啥区别_空格与外边距的使用场景【说明】
焦点电影公司作品,电影焦点结局是什么?
什么是javascript作用域_全局和局部作用域有什么区别?
实例解析angularjs的filter过滤器
java ZXing生成二维码及条码实例分享
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
Laravel如何使用Collections进行数据处理?(实用方法示例)
Android滚轮选择时间控件使用详解
教学论文网站制作软件有哪些,写论文用什么软件
?
Laravel怎么实现搜索高亮功能_Laravel结合Scout与Algolia全文检索【实战】
JavaScript如何实现倒计时_时间函数如何精确控制
Laravel如何使用Passport实现OAuth2?(完整配置步骤)
香港网站服务器数量如何影响SEO优化效果?
米侠浏览器网页图片不显示怎么办 米侠图片加载修复
电商网站制作价格怎么算,网上拍卖流程以及规则?
为什么要用作用域操作符_php中访问类常量与静态属性的优势【解答】
学生网站制作软件,一个12岁的学生写小说,应该去什么样的网站?
网站制作企业,网站的banner和导航栏是指什么?
如何快速搭建自助建站会员专属系统?
微信小程序制作网站有哪些,微信小程序需要做网站吗?
jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】
Laravel怎么返回JSON格式数据_Laravel API资源Response响应格式化【技巧】
Linux系统命令中tree命令详解
JS去除重复并统计数量的实现方法
猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?
Python数据仓库与ETL构建实战_Airflow调度流程详解
如何将凡科建站内容保存为本地文件?
Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】
Swift中swift中的switch 语句
Laravel表单请求验证类怎么用_Laravel Form Request分离验证逻辑教程
JavaScript如何实现错误处理_try...catch如何捕获异常?
Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】
实现点击下箭头变上箭头来回切换的两种方法【推荐】
广州网站制作公司哪家好一点,广州欧莱雅百库网络科技有限公司官网?
Laravel如何清理系统缓存命令_Laravel清除路由配置及视图缓存的方法【总结】
PHP的CURL方法curl_setopt()函数案例介绍(抓取网页,POST数据)
在线ppt制作网站有哪些软件,如何把网页的内容做成ppt?
高端云建站费用究竟需要多少预算?
php485函数参数是什么意思_php485各参数详细说明【介绍】
如何在Windows服务器上快速搭建网站?
如何选择PHP开源工具快速搭建网站?
如何在建站宝盒中设置产品搜索功能?
宙斯浏览器怎么屏蔽图片浏览 节省手机流量使用设置方法
如何快速选择适合个人网站的云服务器配置?
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
如何用免费手机建站系统零基础打造专业网站?
如何自定义建站之星网站的导航菜单样式?
如何在建站之星网店版论坛获取技术支持?
Windows10电脑怎么查看硬盘通电时间_Win10使用工具检测磁盘健康
怎么制作一个起泡网,水泡粪全漏粪育肥舍冬季氨气超过25ppm,可以有哪些措施降低舍内氨气水平?

