news 2026/7/15 2:12:52

HoRain云--深入解析JavaScript闭包:从原理到实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HoRain云--深入解析JavaScript闭包:从原理到实战

🎬 HoRain云小助手:个人主页

🔥 个人专栏: 《Linux 系列教程》《c语言教程》

⛺️生活的理想,就是为了理想的生活!


⛳️ 推荐

前些天发现了一个超棒的服务器购买网站,性价比超高,大内存超划算!忍不住分享一下给大家。点击跳转到网站。

专栏介绍

专栏名称

专栏介绍

《C语言》

本专栏主要撰写C干货内容和编程技巧,让大家从底层了解C,把更多的知识由抽象到简单通俗易懂。

《网络协议》

本专栏主要是注重从底层来给大家一步步剖析网络协议的奥秘,一起解密网络协议在运行中协议的基本运行机制!

《docker容器精解篇》

全面深入解析 docker 容器,从基础到进阶,涵盖原理、操作、实践案例,助您精通 docker。

《linux系列》

本专栏主要撰写Linux干货内容,从基础到进阶,知识由抽象到简单通俗易懂,帮你从新手小白到扫地僧。

《python 系列》

本专栏着重撰写Python相关的干货内容与编程技巧,助力大家从底层去认识Python,将更多复杂的知识由抽象转化为简单易懂的内容。

《试题库》

本专栏主要是发布一些考试和练习题库(涵盖软考、HCIE、HRCE、CCNA等)

目录

⛳️ 推荐

专栏介绍

🔧 形成条件与工作原理

🚀 核心应用场景

1. 数据封装与私有变量

2. 模块模式

3. 函数工厂与柯里化

4. 解决循环中的变量捕获问题

⚠️ 注意事项与性能考量

💎 总结


JavaScript 闭包确实是一个既核心又容易让人困惑的概念。简单来说,闭包就是函数与其词法环境的组合,它让内部函数能够“记住”并访问创建时的作用域,即使外部函数已经执行完毕。

为了让你快速抓住核心,我们先来看一个最经典的例子:

function createCounter() { let count = 0; // 一个局部变量 return function() { count++; // 内部函数引用了外部变量 return count; }; } const myCounter = createCounter(); console.log(myCounter()); // 1 console.log(myCounter()); // 2 console.log(myCounter()); // 3

在这个例子里,count看似是createCounter的局部变量,通常函数执行完就会被清理。但由于createCounter返回了一个内部函数,并且这个内部函数引用count,就形成了一个闭包,使得count能被持续维护和访问。

🔧 形成条件与工作原理

一个典型的闭包需要满足三个条件:

  1. 函数嵌套:一个函数内部定义了另一个函数。

  2. 引用外部变量:内部函数引用了外部函数作用域中的变量(包括参数)。

  3. 外部调用:内部函数在其外部作用域被调用(如被返回、作为回调函数传递等)。

其工作原理关键在于 JavaScript 的词法作用域​ 和垃圾回收机制

🚀 核心应用场景

闭包的应用非常广泛,是其强大能力的体现。

1. 数据封装与私有变量

JavaScript 本身没有类似 Java 的private关键字来直接声明私有变量,但闭包可以完美实现这个功能。

function createPerson(name) { // `_name` 和 `_age` 可以看作是私有变量 let _name = name; let _age = 0; return { getName: function() { return _name; }, getAge: function() { return _age; }, setAge: function(age) { if (age > 0) _age = age; }, celebrateBirthday: function() { _age++; } }; } const person = createPerson('Alice'); console.log(person.getName()); // "Alice" person.setAge(25); person.celebrateBirthday(); console.log(person.getAge()); // 26 // 无法直接访问 _name 或 _age,实现了数据的封装和保护

在这个例子中,外部代码无法直接修改_name_age,必须通过返回的公共方法,这很好地体现了封装性。

2. 模块模式

闭包是早期 JavaScript 实现模块化的基石,可以有效避免全局变量污染。

const Calculator = (function() { // 私有变量和函数 let memory = 0; function add(x, y) { return x + y; } function subtract(x, y) { return x - y; } // 公开的API return { calculate: function(a, b, operator) { const result = operator === '+' ? add(a, b) : subtract(a, b); memory = result; // 可以操作私有变量 return result; }, getMemory: function() { return memory; }, clearMemory: function() { memory = 0; } }; })(); console.log(Calculator.calculate(5, 3, '+')); // 8 console.log(Calculator.getMemory()); // 8 Calculator.clearMemory();

这种模式被称为“立即调用函数表达式”,它创建了一个独立的作用域,只暴露特定的接口给外部。

3. 函数工厂与柯里化

闭包可以用于创建特定配置的函数。

// 函数工厂 function createMultiplier(factor) { return function(x) { return x * factor; }; } const double = createMultiplier(2); const triple = createMultiplier(3); console.log(double(5)); // 10 console.log(triple(5)); // 15 // 简单的柯里化 function add(x) { return function(y) { return x + y; }; } const add5 = add(5); console.log(add5(3)); // 8
4. 解决循环中的变量捕获问题

这是一个经典面试题,展示了var作用域和闭包的相互作用。

// 问题:所有按钮点击都会输出 buttons.length for (var i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // 输出 3, 3, 3 }, 100); } // 解决方案1:使用IIFE和闭包,为每次迭代创建独立作用域 for (var i = 0; i < 3; i++) { (function(j) { // j 捕获了当前循环的 i 值 setTimeout(function() { console.log(j); // 输出 0, 1, 2 }, 100); })(i); } // 解决方案2(现代最佳实践):使用 `let` 声明变量 for (let i = 0; i < 3; i++) { setTimeout(function() { console.log(i); // 输出 0, 1, 2 }, 100); }

let声明的变量具有块级作用域,每次循环都会创建一个新的i绑定,本质上也是一种闭包行为,但写法简洁得多。

⚠️ 注意事项与性能考量

闭包虽好,但也要注意使用方式。

  1. 内存泄漏风险:因为闭包会长期持有外部变量的引用,如果闭包本身的生命周期很长(例如被赋给全局变量或作为事件监听器未移除),那么它引用的所有变量都不会被垃圾回收,可能导致内存占用过高。在不需要闭包时,主动将引用置为null有助于垃圾回收。

  2. 性能考量:闭包会延长作用域链,在查找变量时需要遍历更长的链,理论上会比局部变量查找稍慢。但在绝大多数情况下,这种差异微乎其微,不应成为放弃使用闭包的理由。通常更应关注代码的清晰度和可维护性。

💎 总结

闭包是 JavaScript 的一个强大特性,它允许函数访问并记住其词法作用域,即使函数在其作用域外执行。闭包常用于创建私有变量和实现模块化。理解闭包的关键在于掌握 JavaScript 的词法作用域和函数是一等公民的特性。

希望这些解释和示例能帮助你透彻地理解 JavaScript 闭包。如果你对某个特定应用场景或细节还有疑问,我们可以继续深入探讨。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/1 12:08:43

一句话生成会说话的数字人,Live Avatar黑科技实测

一句话生成会说话的数字人&#xff0c;Live Avatar黑科技实测 1. 引言&#xff1a;一句话唤醒一个“活人” 你有没有想过&#xff0c;只需要一句话、一张图、一段声音&#xff0c;就能让一个虚拟人物在屏幕上开口说话&#xff0c;表情自然、口型精准、动作流畅&#xff1f;这…

作者头像 李华
网站建设 2026/7/13 16:40:29

小白也能懂的YOLOv12:官方镜像快速上手指南

小白也能懂的YOLOv12&#xff1a;官方镜像快速上手指南 你是不是也经历过这样的场景&#xff1f;看到一篇目标检测的新论文&#xff0c;兴致勃勃地想跑个demo&#xff0c;结果光是配置环境就卡了三天&#xff1a;依赖冲突、CUDA版本不匹配、PyTorch装不上……最后只能放弃。 …

作者头像 李华
网站建设 2026/7/1 12:08:49

别再骂量化了!这三类散户,正在悄悄靠它赚钱

一、量化交易是敌是友&#xff1f;谈到“量化交易”&#xff0c;许多散户投资者的第一反应可能是厌恶和恐惧。在大家看来&#xff0c;这股由算法驱动的神秘力量似乎总是在市场上兴风作浪&#xff0c;收割着普通投资者。量化交易真的只是百害而无一利吗&#xff1f;凡事都具有两…

作者头像 李华
网站建设 2026/7/1 12:08:48

setprop测试属性验证法,简化调试流程

setprop测试属性验证法&#xff0c;简化调试流程 在Android系统开发过程中&#xff0c;开机启动脚本的调试是一项常见但容易出错的任务。尤其是在涉及Selinux权限、执行路径、脚本语法等问题时&#xff0c;传统通过创建文件或打印日志的方式往往难以快速定位问题。本文将介绍一…

作者头像 李华
网站建设 2026/7/14 20:10:29

iOSDeviceSupport终极解决方案:告别Xcode调试兼容性困扰

iOSDeviceSupport终极解决方案&#xff1a;告别Xcode调试兼容性困扰 【免费下载链接】iOSDeviceSupport All versions of iOS Device Support 项目地址: https://gitcode.com/gh_mirrors/ios/iOSDeviceSupport 还在为Xcode提示"无法定位设备支持文件"而烦恼吗…

作者头像 李华