news 2026/6/10 16:08:12

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/6/10 15:32:53

无需GPU高手!VibeVoice普通电脑也能跑

无需GPU高手&#xff01;VibeVoice普通电脑也能跑 1. 普通电脑也能玩转AI语音&#xff1f;微软这个开源项目太亲民了 你是不是也曾经以为&#xff0c;生成高质量的多角色对话音频&#xff0c;非得配一张顶级显卡不可&#xff1f; 动辄几十GB显存、上千元云服务费用、复杂的命…

作者头像 李华
网站建设 2026/6/10 15:37:43

惠普游戏本性能释放终极指南:OmenSuperHub完全掌控方案

惠普游戏本性能释放终极指南&#xff1a;OmenSuperHub完全掌控方案 【免费下载链接】OmenSuperHub 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 还在为官方OMEN控制中心的卡顿和广告烦恼吗&#xff1f;一款真正懂玩家的开源神器——OmenSuperHub&#x…

作者头像 李华
网站建设 2026/6/10 14:34:44

小白也能玩转目标检测:YOLOv10镜像极简入门

小白也能玩转目标检测&#xff1a;YOLOv10镜像极简入门 你是不是也曾经被“目标检测”这个词吓退&#xff1f;觉得这玩意儿得懂深度学习、会调参、还得折腾环境&#xff1f;其实&#xff0c;现在只要一个镜像&#xff0c;几分钟就能让你跑通最先进的目标检测模型。 今天要介绍…

作者头像 李华
网站建设 2026/6/10 16:20:59

启用8bit量化后,Qwen3-1.7B终于能在手机跑了

启用8bit量化后&#xff0c;Qwen3-1.7B终于能在手机跑了 1. 引言&#xff1a;轻量级大模型的移动化突破 你有没有想过&#xff0c;一个拥有17亿参数的大语言模型&#xff0c;可以在你的手机上本地运行&#xff1f;这不再是科幻场景。随着Qwen3-1.7B推出并支持8bit量化&#x…

作者头像 李华
网站建设 2026/6/10 11:19:47

Qwen3-0.6B实时翻译系统:低延迟推理部署优化案例

Qwen3-0.6B实时翻译系统&#xff1a;低延迟推理部署优化案例 1. 轻量级大模型的实时应用潜力 在AI落地场景中&#xff0c;响应速度往往比模型规模更重要。尤其是在实时翻译、语音交互、边缘计算等对延迟敏感的应用中&#xff0c;一个体积小但响应快的模型&#xff0c;远比“大…

作者头像 李华
网站建设 2026/6/10 0:34:00

【Docker-compose环境变量高级用法】:99%开发者忽略的5个最佳实践技巧

第一章&#xff1a;Docker-compose环境变量的核心机制与加载原理 Docker Compose 通过环境变量实现配置的灵活注入&#xff0c;使得应用在不同环境中具备高度可移植性。其核心机制依赖于 .env 文件、environment 字段以及 env_file 指令的协同工作&#xff0c;按特定优先级顺序…

作者头像 李华