閉包的定義和應用場景

一、閉包是什么?

閉包是指函數可以“記住”并訪問它定義時的詞法作用域,即使這個函數在其作用域鏈之外執行。
簡單說:函數 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
英文地址,請注明出處:http://en.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…