JavaScript篇:闭包:JavaScript中的魔法口袋,装下你的编程智慧
大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。
我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
目录
什么是闭包?
闭包的五大实用场景
1. 数据封装与私有变量
2. 函数工厂
3. 事件处理与回调
4. 模块模式
5. 记忆化(Memoization)优化
闭包的常见误区
1. 循环中的闭包陷阱
2. 内存泄漏风险
现代JavaScript中的闭包
性能考量
结语
作为一名前端开发者,我至今还记得第一次理解闭包时那种"啊哈!"的顿悟时刻。闭包就像是JavaScript送给我们的一个魔法口袋,看起来简单,却能装下无穷的编程智慧。今天,就让我来为你揭开这个魔法口袋的秘密。
什么是闭包?
简单来说,闭包就是能够访问其他函数内部变量的函数。就像我有一个私人保险箱(函数内部的变量),然后给了你一把钥匙(返回的函数),这样即使我离开了(外部函数执行完毕),你依然可以打开保险箱访问里面的东西。
function createCounter() {let myCount = 0; // 这个变量将被"封闭"在返回的函数中return function() {myCount++;return myCount;};
}const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
闭包的五大实用场景
1. 数据封装与私有变量
在ES6之前,JavaScript没有原生的私有成员概念,闭包帮我们实现了这一点:
function createPerson(myName) {let age = 0; // 私有变量return {getName: function() {return myName;},getAge: function() {return age;},celebrateBirthday: function() {age++;return `Happy birthday, ${myName}! You're now ${age} years old.`;}};
}const me = createPerson('John');
console.log(me.getName()); // "John"
console.log(me.getAge()); // 0
console.log(me.celebrateBirthday()); // "Happy birthday, John! You're now 1 years old."
console.log(me.age); // undefined - 无法直接访问
2. 函数工厂
闭包让我们可以轻松创建功能相似但配置不同的函数:
function createMultiplier(factor) {return function(number) {return number * factor;};
}const double = createMultiplier(2);
const triple = createMultiplier(3);console.log(double(5)); // 10
console.log(triple(5)); // 15
3. 事件处理与回调
闭包在事件处理中特别有用,可以记住创建时的上下文:
function setupButtons() {const colors = ['red', 'green', 'blue'];for (var i = 0; i < colors.length; i++) {// 使用IIFE创建闭包来捕获每个迭代的i值(function(index) {document.getElementById(`btn-${index}`).addEventListener('click', function() {console.log(`You clicked the ${colors[index]} button`);});})(i);}
}// 现代写法可以用let替代IIFE
function setupButtonsModern() {const colors = ['red', 'green', 'blue'];for (let i = 0; i < colors.length; i++) {document.getElementById(`btn-${i}`).addEventListener('click', function() {console.log(`You clicked the ${colors[i]} button`);});}
}
4. 模块模式
闭包是实现模块化的基础,在ES6之前是主要的模块化方案:
const myModule = (function() {const privateVar = 'I am private';function privateMethod() {console.log(privateVar);}return {publicMethod: function() {privateMethod();},publicVar: 'I am public'};
})();console.log(myModule.publicVar); // "I am public"
myModule.publicMethod(); // "I am private"
console.log(myModule.privateVar); // undefined
5. 记忆化(Memoization)优化
闭包可以用来缓存昂贵的函数调用结果:
function memoize(fn) {const cache = {};return function(...args) {const key = JSON.stringify(args);if (cache[key] !== undefined) {console.log('Fetching from cache');return cache[key];} else {console.log('Calculating result');const result = fn.apply(this, args);cache[key] = result;return result;}};
}// 一个计算量大的函数
function expensiveCalculation(n) {console.log('Performing expensive calculation...');return n * n;
}const memoizedCalculation = memoize(expensiveCalculation);console.log(memoizedCalculation(5)); // 计算并缓存
console.log(memoizedCalculation(5)); // 从缓存读取
闭包的常见误区
1. 循环中的闭包陷阱
// 常见错误示例
for (var i = 0; i < 3; i++) {setTimeout(function() {console.log(i); // 全部输出3!}, 100);
}// 解决方案1:使用IIFE
for (var i = 0; i < 3; i++) {(function(index) {setTimeout(function() {console.log(index); // 0, 1, 2}, 100);})(i);
}// 解决方案2:使用let
for (let i = 0; i < 3; i++) {setTimeout(function() {console.log(i); // 0, 1, 2}, 100);
}
2. 内存泄漏风险
闭包会阻止垃圾回收器回收被引用的变量,不当使用可能导致内存泄漏:
// 可能导致内存泄漏的示例
function setupHugeData() {const hugeData = getHugeData(); // 获取大量数据return function() {// 即使外部不需要hugeData了,闭包仍保持引用doSomethingWith(hugeData.smallPart);};
}// 解决方案:在不需要时手动解除引用
function setupHugeDataSafe() {const hugeData = getHugeData();const smallPart = hugeData.smallPart;// 不再保留对hugeData的引用hugeData = null;return function() {doSomethingWith(smallPart);};
}
现代JavaScript中的闭包
随着ES6+的普及,闭包的使用变得更加简洁优雅:
// 使用箭头函数
const createAdder = (x) => (y) => x + y;
const add5 = createAdder(5);
console.log(add5(3)); // 8// 结合解构
const createUser = ({ firstName, lastName }) => ({getFullName: () => `${firstName} ${lastName}`,setLastName: (newLastName) => { lastName = newLastName; }
});const user = createUser({ firstName: 'John', lastName: 'Doe' });
console.log(user.getFullName()); // "John Doe"
user.setLastName('Smith');
console.log(user.getFullName()); // "John Smith"
性能考量
闭包不是免费的午餐,使用时需要考虑:
-
内存消耗:闭包会保持对外部变量的引用,阻止垃圾回收
-
创建速度:闭包的创建比普通函数稍慢
-
优化限制:某些JavaScript引擎对闭包的优化不如普通函数
但在大多数情况下,这些开销可以忽略不计,闭包带来的好处远大于成本。
结语
闭包是JavaScript中最强大也最容易被误解的特性之一。它就像是一把瑞士军刀,小巧但功能多样。理解闭包不仅能让你写出更优雅的代码,还能帮助你深入理解JavaScript的语言本质。
记住,闭包不是用来炫技的工具,而是解决特定问题的利器。当你需要封装数据、创建函数工厂、处理回调时,不妨想想这个"魔法口袋"是否能帮上忙。
你在项目中用过哪些有趣的闭包应用?或者遇到过哪些闭包的"坑"?欢迎在评论区分享你的故事!
相关文章:
JavaScript篇:闭包:JavaScript中的魔法口袋,装下你的编程智慧
大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了…...

ubuntu系统安装Pyside6报错解决
目录 1,问题: 2,解决方法: 2.1 首先查看pypi是否有你需要包的镜像: 2.2 其它方案: 2.3 如果下载很慢,可以换源: 2.4 查看系统架构 Windows Ubuntu 1,问题…...
DeepSeek 赋能智能零售:从数据洞察到商业革新
目录 一、智能零售的现状与挑战二、DeepSeek 技术特点剖析2.1 基于 Transformer 架构的深度优化2.2 多源数据的深度分析能力2.3 强大的学习与推理能力 三、DeepSeek 在智能零售中的应用场景3.1 精准需求预测3.2 智能补货决策3.3 库存优化布局3.4 个性化推荐与营销3.5 智能客服与…...

榕壹云医疗服务系统:基于ThinkPHP+MySQL+UniApp的多门店医疗预约小程序解决方案
在数字化浪潮下,传统医疗服务行业正面临效率提升与客户体验优化的双重挑战。针对口腔、美容、诊所、中医馆、专科医院及康复护理等需要预约或诊断服务的行业,我们开发了一款基于ThinkPHP+MySQL+UniApp的多门店服务预约小程序——榕壹云医疗服务系统。该系统通过模块化设计与开…...

苏州SAP代理公司排名:工业园区企业推荐的服务商
目录 一、SAP实施商选择标准体系 1、行业经验维度 2、实施方法论维度 3、资质认证维度 4、团队实力维度 二、SAP苏州实施商工博科技 1、SAP双重认证,高等院校支持 2、以SAP ERP为核心,助力企业数字化转型 三、苏州使用SAP的企业 苏州是中国工业…...

数据结构中无向图的邻接矩阵详解
在计算机科学的浩瀚宇宙中,数据结构无疑是那把开启高效编程大门的关键钥匙。对于计算机专业的大学生们来说,数据结构课程是专业学习路上的一座重要里程碑,而其中的图结构更是充满魅力与挑战,像一幅神秘的画卷等待我们去展开。今天…...

.NET 7 AOT 使用及 .NET 与 Go 语言互操作详解
.NET 7 AOT 使用及 .NET 与 Go 语言互操作详解 目录 .NET 7 AOT 使用及 .NET 与 Go 语言互操作详解 一、背景与技术概述 1.1 AOT 编译技术简介 1.2 Go 语言与 .NET 的互补性 二、.NET 7 AOT 编译实践 2.1 环境准备 2.2 创建 AOT 项目 2.3 AOT 编译流程 2.4 调试信息处…...

OpenCV 第7课 图像处理之平滑(一)
1. 图像噪声 在采集、处理和传输过程中,数字图像可能会受到不同噪声的干扰,从而导致图像质量降低、图像变得模糊、图像特征被淹没,而图像平滑处理就是通过除去噪声来达到图像增强的目的。常见的图像噪声有椒盐噪声、高斯噪声等。 1.1 椒盐噪声 椒盐噪声(Salt-and-pepper N…...

React 编译器
🤖 作者简介:水煮白菜王,一位前端劝退师 👻 👀 文章专栏: 前端专栏 ,记录一下平时在博客写作中,总结出的一些开发技巧和知识归纳总结✍。 感谢支持💕💕&#…...

HCIP:MPLS静态LSP的配置及抓包
目录 一、MPLS的简单的一些知识点 1.MPLS的概述: 2.MPLS工作原理: 3.MPLS的核心组件: 4. MPLS标签 5.MPLS标签的处理 6.MPLS转发的概述: 7.MPLS的静态LSP建立方式 二、MPLS的静态LSP的实验配置 1.配置接口的地址和配置OS…...

VASP 教程:VASP 结合 Phonopy 计算硅的比热容
VASP 全称为 Vienna Ab initio Simulation Package(The VASP Manual - VASP Wiki)是一个计算机程序,用于从第一性原理进行原子尺度材料建模,例如电子结构计算和量子力学分子动力学。 Phonopy(Welcome to phonopy — Ph…...
YOLO使用SAHI进行小目标检测
目录 一、环境配置二、使用ultralytics的YOLO模型进行训练和推理三、推理可视化的两种方法四、使用SAHI和ultralytics 训练的YOLO模型进行推理一、环境配置 下面是环境的配置过程,根据代码复杂度可以额外安装其他包。 #创建虚拟环境 conda create -n 环境名 python=3.9 #开启…...

[论文阅读]Prompt Injection attack against LLM-integrated Applications
Prompt Injection attack against LLM-integrated Applications [2306.05499] Prompt Injection attack against LLM-integrated Applications 传统提示注入攻击效果差,主要原因在于: 不同的应用对待用户的输入内容不同,有的将其视为问题&a…...
【SpringCache 提供的一套基于注解的缓存抽象机制】
Spring 缓存(Spring Cache)是 Spring 提供的一套基于注解的缓存抽象机制,常用于提升系统性能、减少重复查询数据库或接口调用。 ✅ 一、基本原理 Spring Cache 通过对方法的返回结果进行缓存,后续相同参数的调用将直接从缓存中读…...

DALI DT6与DALI DT8介绍
“DT”全称Device Type,是DALI-2 标准协议中的IEC 62386-102(即为Part 102)部分对不同类型的控制设备进行一个区分。不同的Device Type代表不同特性的控制设备,也代表了这种控制设备拥有的扩展的特性。 在DALI(数字可寻址照明接口)…...

day13 leetcode-hot100-24(链表3)
234. 回文链表 - 力扣(LeetCode) 1.转化法 思路 将链表转化为列表进行比较 复习到的知识 arraylist的长度函数:list.size() 具体代码 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode ne…...
Python实战:打造高效通讯录管理系统
📋 编程基础第一期《8-30》–通讯录管理系统 📑 项目介绍 在信息化时代,高效管理个人或团队联系人信息变得尤为重要。本文将带您实现一个基于Python的通讯录管理系统,该系统采用字典数据结构和JSON文件存储,实现了联系…...

图解深度学习 - 基于梯度的优化(梯度下降)
在模型优化过程中,我们曾尝试通过手动调整单个标量系数来观察其对损失值的影响。具体来说,当初始系数为0.3时,损失值为0.5。随后,我们尝试增加系数至0.35,发现损失值上升至0.6;相反,当系数减小至…...

MySql--定义表存储引擎、字符集和排序规则
示例: CREATE TABLE users (id INT PRIMARY KEY,name VARCHAR(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci,email VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci;注意事项: 字符集和排序规则可以按列覆盖表…...
【部署】在离线服务器的docker容器下升级dify-import程序
回到目录 在离线服务器的docker容器下升级dify-import程序 dify 0.1.0-release 变化很大,重构整个项目代码并且增加制度类txt文件知识库父子分段支持,详见 读取制度类txt文件导入dify的父子分段知识库(20250526发布). 。下面是kylin Linux环境下&#…...

优化版本,增加3D 视觉 查看前面的记录
上图先 运来的超出发表上限,重新发。。。 #11:06:57Current_POS_is: X:77Y:471Z:0U:-2 C:\Log\V55.txt import time import tkinter as tk from tkinter import messagebox from PIL import Image, ImageTk import socket import threading from date…...
写作-- 复合句练习
文章目录 练习 11. 家庭的支持和老师的指导对学生的学术成功有积极影响。2. 缺乏准备和未能适应通常会导致在挑战性情境中的糟糕表现。3. 吃垃圾食品和忽视锻炼可能导致严重的健康问题,因此人们应注重保持均衡的生活方式。4. 昨天的大雨导致街道洪水泛滥,因此居民们迁往高地以…...

WWW22-可解释推荐|用于推荐的神经符号描述性规则学习
论文来源:WWW 2022 论文链接:https://web.archive.org/web/20220504023001id_/https://dl.acm.org/doi/pdf/10.1145/3485447.3512042 最近读到一篇神经符号集成的论文24年底TOIS的,神经符号集成是人工智能领域中,将符号推理与深…...

Linux:shell脚本常用命令
一、设置主机名称 1、查看主机名称 2、用文件的方式更改主机名称 重启后: 3、 通过命令修改主机名 重启后: 二、网络管理命令 1、查看网卡 2、设置网卡 (1)网卡未被设置过时 (2)当网卡被设定,…...
专业课复习笔记 11
从今天开始每天下午复习专业课。慢慢复习专业课。目标至少考一个一百分吧。毕竟专业课还是比较难的。要是考不到一百分,我感觉自己就废掉了呢。下面稍微复习一下计组。 复习指令格式和数据通路设计。完全看不懂,真是可恶啊。计组感觉就是死记硬背&#…...

OpenTelemetry × Elastic Observability 系列(一):整体架构介绍
本文是 OpenTelemetry Elastic Observability 系列的第一篇,将介绍 OpenTelemetry Demo 的整体架构,以及如何集成 Elastic 来采集和可视化可观测性数据。后续文章将分别针对不同编程语言,深入讲解 OpenTelemetry 的集成实践。 程序架构 Op…...

STM32高级物联网通信之以太网通讯
目录 以太网通讯基础知识 什么是以太网 互联网和以太网的区别 1)概念与范围 (1)互联网 (2)以太网 2)技术特点 (1)互联网 (2)以太网 3)应…...
从Java的Jvm的角度解释一下为什么String不可变?
从Java的Jvm的角度解释一下为什么String不可变? 从 JVM 的角度看,Java 中 String 的不可变性是由多层次的机制共同保障的,这些设计涉及内存管理、性能优化和安全保障: 1. JVM 内存模型与字符串常量池 字符串常量池(St…...
从零开始的数据结构教程(四) 图论基础与算法实战
🌐 标题一:图的表示——六度空间理论如何用代码实现? 核心需求 图(Graph)是用于表达实体间关系的强大数据结构,比如社交网络中的好友关系,或者城市路网的交叉路口连接。关键在于如何高效存储和…...

历年西安交通大学计算机保研上机真题
2025西安交通大学计算机保研上机真题 2024西安交通大学计算机保研上机真题 2023西安交通大学计算机保研上机真题 在线测评链接:https://pgcode.cn/school 计算圆周率近似值 题目描述 根据公式 π / 4 1 − 1 / 3 1 / 5 − 1 / 7 … \pi / 4 1 - 1/3 1/5 - …...