闭包的定义和应用场景

一、闭包是什么?

闭包是指函数可以“记住”并访问它定义时的词法作用域,即使这个函数在其作用域链之外执行。
简单说:函数 A 在函数 B 中被定义,并在函数 B 外部被调用,它依然能访问函数 B 中的变量,这就是闭包。

示例:

function outer() {let count = 0;return function inner() {count++;console.log(count);};
}const counter = outer();  // outer 执行,返回 inner
counter(); // 1
counter(); // 2
  • counter 是 inner 函数。
  • 虽然 outer 已经执行完毕,但 inner 仍能访问 outer 中的变量 count。
  • 因为 JS 引擎保留了 outer 的词法作用域 —— 这就是闭包。

二、闭包的核心特性

  1. 函数嵌套函数
  2. 内部函数引用了外部函数的变量
  3. 外部函数执行后,其内部作用域仍被保留

三、常见的闭包封装类型

1. 封装私有变量

  • 避免全局污染,创建私有作用域,保护变量不被外部修改。

示例:

function createCounter() {let count = 0;return {increment() {count++;console.log(count);},decrement() {count--;console.log(count);}};
}const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.decrement(); // 1

2. 防抖函数(Debounce)

  • 控制函数的执行频率,避免频繁触发(如搜索输入框)

示例:

function debounce(fn, delay) {let timer = null;return function (...args) {clearTimeout(timer);timer = setTimeout(() => {fn.apply(this, args);}, delay);};
}

实际应用场景:

  1. 搜索输入联想(autocomplete)
    • 描述:用户输入关键词时,实时发送接口请求搜索建议;
    • 问题:用户每打一个字就触发请求,接口压力大;
    • 防抖:用户停止输入一段时间(如 300ms)后再发起请求。
<input type="text" oninput="debounceSearch(event)">const debounceSearch = debounce((e) => {searchAPI(e.target.value);
}, 300);
  1. 窗口大小调整(resize)事件
    • 描述:监听 window.onresize 调整布局;
    • 问题:调整过程中会疯狂触发;
    • 防抖:等用户停止调整再触发逻辑。
window.addEventListener('resize', debounce(() => {updateLayout();
}, 200));
  1. 表单校验(输入完成后校验)
    • 输入过程中不断校验字段太频繁;
    • 防抖校验:等用户停止输入后再校验格式/是否重复。

3. 节流函数(Throttle)

  • 限制函数在某段时间内只执行一次,常用于滚动/resize等事件。

示例:

function throttle(fn, delay) {let lastTime = 0;return function (...args) {const now = Date.now();if (now - lastTime > delay) {fn.apply(this, args);lastTime = now;}};
}

实际应用场景:

  1. 页面滚动事件(scroll)
    • 描述:滚动时触发监听函数,计算位置、懒加载、吸顶等;
    • 问题:滚动过程中触发频繁,影响性能;
    • 节流:限制函数每隔 100ms 触发一次。
window.addEventListener('scroll', throttle(() => {checkLoadMore();
}, 100));
  1. 按钮点击防止重复提交
    • 描述:用户频繁点击“提交”按钮;
    • 节流:按钮点击1秒内只能触发一次。
<button onclick="throttleSubmit()">提交</button>const throttleSubmit = throttle(() => {submitForm();
}, 1000);
  1. 拖拽事件
    • 拖动一个 DOM 元素时,mousemove 触发频繁;
    • 节流避免频繁 DOM 操作。

防抖和节流小结

技术关键词作用
防抖(debounce)最后一次等用户停止操作一段时间后再执行
节流(throttle)每隔一次控制函数在一定时间内最多执行一次

4. 缓存函数(记忆函数)

  • 对重复计算进行缓存优化(如递归计算斐波那契数)

示例:

function memoize(fn) {const cache = {};return function (key) {if (cache[key] !== undefined) {return cache[key];}const result = fn(key);cache[key] = result;return result;};
}const fib = memoize(function(n) {console.log("计算 fib(" + n + ")");if (n <= 1) return n;return fib(n - 1) + fib(n - 2);
});console.log(fib(5)); // 会打印很多“计算 fib(n)”
console.log(fib(5)); // 这次会直接用缓存,打印很少

5. 单例模式封装

  • 只创建一次实例(如弹窗、全局组件)

示例:

function getSingleton(fn) {let instance;return function (...args) {if (!instance) {instance = fn.apply(this, args);}return instance;};
}const createDialog = getSingleton(() => {const div = document.createElement('div');div.innerHTML = '我是弹窗';document.body.appendChild(div);return div;
});
  • 第一次调用:instance 为 undefined,会执行 fn,并把返回值保存在 instance 中;
  • 后续调用:直接返回第一次的 instance,不再执行 fn。

6. 柯里化函数(Currying)

  • 把接受多个参数的函数,转换成一系列接受单个参数的函数。

示例:

function curry(fn) {return function curried(...args) {if (args.length >= fn.length) {return fn(...args);}return function (...next) {return curried(...args, ...next);};};
}function add(a, b, c) {return a + b + c;
}const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 也可以,输出 6
console.log(curriedAdd(1)(2, 3)); // 也可以,输出 6

7. 工厂函数(封装状态)

  • 用于组件、模块创建,管理状态

示例:

function createUser(name) {let _name = name;return {getName() {return _name;},setName(newName) {_name = newName;}};
}const user = createUser('77');
console.log(user.getName()); // 77
user.setName('88');
console.log(user.getName()); // 88

8. 延迟计算 / 延迟执行

  • 按需执行逻辑,提高性能或解决作用域问题

示例:

function lazy(fn) {let cached = null;return function () {if (cached === null) {cached = fn();}return cached;};
}const getConfig = lazy(() => {console.log('读取配置...');return { env: 'prod' };
});getConfig(); // 读取配置... 
getConfig(); // (不再重复执行)

四、闭包的注意事项

  • 内存泄漏风险:闭包会长期保留作用域链,如果引用过多变量未释放,可能导致内存问题。
  • 调试难度稍高:变量作用域不明显时,排查问题可能会变复杂。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/89649.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/89649.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

北京-4年功能测试2年空窗-报培训班学测开-第五十四天

今天交付的成果是&#xff0c;初版简历虽然只写了项目部分&#xff0c;但用了一整天&#xff0c;期间联系了前司组长&#xff0c;拿到了性能测试报告。然后再看压测脚本&#xff0c;突然能看懂了&#xff0c;对服务端日志也能看懂些了&#xff0c;还找到了客户端日志怎么说呢&a…

算法训练营day24 回溯算法③ 93.复原IP地址 、78.子集、 90.子集II

今天继续回溯算法的专题&#xff0c;第三篇博客&#xff01; 93.复原IP地址 输入&#xff1a;s "25525511135" 输出&#xff1a;["255.255.11.135","255.255.111.35"] 切割字符串为4段&#xff0c;当进行到第四段的时候对第四段字符串进行判断…

jeccg-boot框架实现xls模板导出功能

文章目录一、后端部分二、前端部分三、模板制作一、后端部分 //1、在application-dev.yml文件增加模板路径path :#模板路径saxls: /data/opt/saxls/ //2、控制层写法 public class sabassalController extends JeecgController<sabassalVo, IsabassalService> {Autowired…

LangChain4j入门:Java开发者的AI应用开发指南

&#x1f680; 在AI浪潮席卷全球的今天&#xff0c;Java开发者如何快速上手大语言模型应用开发&#xff1f;LangChain4j为我们提供了完美的解决方案&#xff01; 前言&#xff1a;为什么Java开发者需要LangChain4j&#xff1f; 想象一下&#xff0c;你正在开发一个企业级应用&…

相机光学(五十)——Depth AF

1.什么是Depth AFDepth AF&#xff08;景深自动对焦&#xff09;&#xff0c;也称为 Depth-of-Field AF&#xff08;景深对焦&#xff09; 或 DEP AF&#xff0c;是一种基于景深范围的自动对焦技术&#xff0c;核心目标是&#xff1a;确保从前景到背景的一整段距离都在清晰景深…

Unity 堆栈分析实战指南 C#

Unity 堆栈分析实战指南 提示&#xff1a;内容纯个人编写&#xff0c;欢迎评论点赞&#xff0c;来指正我。 文章目录Unity 堆栈分析实战指南1. 前言2. 什么是堆栈3. Unity 中的堆栈4. 堆栈分析工具5. 如何进行堆栈分析6. 实战案例分析案例 1: 性能瓶颈分析案例 2: 内存泄漏检测…

AE MDX L6 L12 L18 电源手侧操作使用说明

AE MDX L6 L12 L18 电源手侧操作使用说明

Gemini Function Calling 和 Qwen3 Embedding和ReRanker模型

Gemini API 的函数调用&#xff08;Function Calling&#xff09;功能。它解决了传统大语言模型&#xff08;LLM&#xff09;的一个关键局限&#xff1a;LLM 本身是基于训练数据的“知识库”&#xff0c;擅长生成文本和回答问题&#xff0c;但无法直接执行代码、访问实时数据或…

​​VMware Workstation Pro 17.5.0 安装教程 - 详细步骤图解(附下载+激活)​

VMware Workstation Pro 17.5.0 是一款功能强大的虚拟机软件&#xff0c;允许用户在一台计算机上同时运行多个操作系统&#xff08;如 Windows、Linux、macOS&#xff09;&#xff0c;适用于开发、测试、运维及学习环境搭建。本教程提供 ​​详细安装步骤​​&#xff0c;包括 …

端到端神经网络视频编解码器介绍

一、技术演进&#xff1a;从模块优化到全局智能的范式跃迁 传统编解码器的效率天花板&#xff08;1990-2017&#xff09; 架构局限&#xff1a;H.264/HEVC依赖手工设计的运动估计、DCT变换、熵编码模块&#xff0c;各模块独立优化导致全局效率损失。高分辨率瓶颈&#xff1a;4…

Kubernetes (k8s)环境重启Pod方式总结

前言&#xff1a;在 Kubernetes (k8s) 中&#xff0c;没有直接的命令如 kubectl restart pod 来重启 Pod&#xff0c;因为 Pod 的生命周期由控制器&#xff08;如 Deployments、StatefulSets 或 ReplicaSets&#xff09;管理。重启操作本质上是通过删除并重建 Pod 来实现的&…

OOA、OOD 与 OOP:面向对象范式的核心支柱详解

作为软件系统架构的核心范式&#xff0c;面向对象方法贯穿软件开发生命周期。OOA、OOD 和 OOP 分别代表分析、设计和实现三个关键阶段&#xff0c;共同构成一个连贯的工程体系。一、OOA (Object-Oriented Analysis&#xff0c;面向对象分析) 目标&#xff1a;理解问题域&#x…

GBase 8a 与 Spring Boot + MyBatis 整合实战:从环境搭建到CRUD操作

一、引言 在企业级数据管理场景中&#xff0c;GBase数据库凭借其高性能的数据分析能力和对SQL标准的良好兼容性&#xff0c;成为金融、电信等行业的常用选择。本文将详细演示如何将GBase数据库与Spring Boot、MyBatis框架整合&#xff0c;实现高效的数据持久化操作&#xff0c…

功能安全之BIST的基本原理

BIST&#xff08;Built-In Self-Test&#xff0c;内建自测试&#xff09;是一种将测试功能直接集成到集成电路&#xff08;IC&#xff09;或系统内部的设计方法。其基本原理的核心在于&#xff1a;让被测试电路自身&#xff08;或借助少量专用硬件&#xff09;来生成测试激励、…

Linux 程序地址空间

目录 Ⅰ、什么是程序地址空间&#xff1f; Ⅱ、虚拟地址空间是什么样的&#xff1f; 一、虚拟地址空间和页表 1、什么是页表&#xff1f; 2、什么是虚拟地址空间&#xff1f; 3、什么是vm_area_struct? Ⅲ、为什么要用虚拟地址空间&#xff1f; 一、进程的独立性 二、…

【iOS】消息传递和消息转发

文章目录前言一、消息传递&#xff1a;objc_msgSend 的“查字典递归找家长”流程1. 第一步&#xff1a;查“最近调用记录”&#xff08;方法缓存&#xff09;—— 最快即快速查找&#xff01;2. 第二步&#xff1a;翻“自己的字典”&#xff08;类方法列表查找&#xff09;——…

MySQL查询优化与事务实战指南

本节用到的员工信息管理表结构放到资源中&#xff0c;需要的同学自取。本节内容以此表为示例&#xff1a; 面试题&#xff1a;innodb与myisam的区别。 外键&#xff0c;事务 特性InnoDBMyISAM事务支持支持不支持外键支持不支持锁粒度行级锁表级锁索引结构聚簇索引非聚簇索引崩…

Windows 10/11 磁盘清理操作指南:彻底解决系统盘空间不足问题

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家、CSDN平台优质创作者&#xff0c;高级开发工程师&#xff0c;数学专业&#xff0c;10年以上C/C, C#,Java等多种编程语言开发经验&#xff0c;拥有高级工程师证书&#xff1b;擅长C/C、C#等开发语言&#xff0c;熟悉Java常用开发…

b-up:Enzo_Mi:深度学习基础知识

1.最近邻差值&#xff08;Neareast Neighbor Interpolation&#xff09; 插值算法 &#xff5c; 最近邻插值法_哔哩哔哩_bilibili 上图中最后一行&#xff0c;第一个图像&#xff0c;因为目标像素&#xff08;放大后&#xff0c;位于第1行第0列的像素&#xff09;距离它最近的…

微信小程序商品结算功能

整体结算流程概述微信小程序的商品结算涉及前端交互、API调用和数据管理。典型流程包括&#xff1a;用户交互&#xff1a;用户选择商品、填写地址和时间。数据获取&#xff1a;从小程序缓存或后端服务器获取订单信息。逻辑处理&#xff1a;验证参数、应用红包折扣。提交订单&am…