news 2026/4/28 1:04:23

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/4/20 8:33:18

Obsidian科研知识管理模板:从零开始构建高效工作流

Obsidian科研知识管理模板&#xff1a;从零开始构建高效工作流 【免费下载链接】obsidian_vault_template_for_researcher This is an vault template for researchers using obsidian. 项目地址: https://gitcode.com/gh_mirrors/ob/obsidian_vault_template_for_researcher…

作者头像 李华
网站建设 2026/4/25 23:32:29

Cursor Pro免费解锁终极指南:一键激活AI编程助手完整方案

Cursor Pro免费解锁终极指南&#xff1a;一键激活AI编程助手完整方案 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached your …

作者头像 李华
网站建设 2026/4/22 11:47:33

Z-Image-Turbo镜像使用:7860端口冲突解决实战案例

Z-Image-Turbo镜像使用&#xff1a;7860端口冲突解决实战案例 Z-Image-Turbo是阿里巴巴通义实验室开源的高效AI图像生成模型&#xff0c;作为Z-Image的蒸馏版本&#xff0c;它以极快的生成速度&#xff08;仅需8步&#xff09;、卓越的图像质量&#xff08;具备照片级真实感&a…

作者头像 李华
网站建设 2026/4/25 17:42:54

3分钟实现百度网盘全速下载的零成本方案

3分钟实现百度网盘全速下载的零成本方案 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘蜗牛般的下载速度而苦恼吗&#xff1f;作为macO…

作者头像 李华
网站建设 2026/4/24 9:56:09

Cursor Free VIP终极方案:2025年完全免费解锁AI编程助手全功能

Cursor Free VIP终极方案&#xff1a;2025年完全免费解锁AI编程助手全功能 【免费下载链接】cursor-free-vip [Support 0.45]&#xff08;Multi Language 多语言&#xff09;自动注册 Cursor Ai &#xff0c;自动重置机器ID &#xff0c; 免费升级使用Pro 功能: Youve reached …

作者头像 李华