閉包的理解

一、閉包的概念

當通過調用外部函數返回的內部函數后,即使外部函數已經執行結束了,但是被內部函數引用的外部函數的變量依然會保存在內存中,我們把引用了其他函數作用域變量的函數和這些被引用變量的集合,稱為閉包(Closure),閉包是這些東西共同的組合
在了解閉包的概念和用途之前,理解作用域和變量的生命周期等基礎預備知識,對于理解閉包非常有幫助。

二、怎么實現閉包
閉包是指一個函數可以訪問它定義時所在的詞法作用域以及全局作用域中的變量。在JavaScript中,閉包可以通過函數嵌套和變量引用實現

function outerFunction() {let outerVariable = '我在outer函數里!';
<span class="token keyword">function</span> <span class="token function">innerFunction</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>outerVariable<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token keyword">return</span> innerFunction<span class="token punctuation">;</span>

}

const innerFunc = outerFunction();
innerFunc(); // 輸出: 我在outer函數里!

在上面的代碼示例中,innerFunction引用了outerVariable,因此JavaScript引擎會保留outerFunction的作用域鏈,以便innerFunction可以訪問outerVariable。

function a(){function b(){var bb = 888console.log(aa);  //輸出:666}var aa = 666return b
}
var demo = a()
demo()

在上面的代碼示例中,a函數定義了一個名為aa的變量和一個名為b的函數,b函數引用了aa變量,因此JavaScript引擎會保留a函數的作用域鏈,b函數可以訪問a函數的執行上下文,b函數內用到了外部函數a的變量aa,在a函數調用結束后該函數執行上下文會銷毀,但會保留一部分留在內存中供b函數使用,這就形成了閉包。

具體來說,當內部函數引用外部函數的變量時,外部函數的作用域鏈將被保留在內存中,以便內部函數可以訪問這些變量。

這種函數嵌套和變量共享的方式就是閉包的核心概念。當一個函數返回另一個函數時,它實際上返回了一個閉包,其中包含了原函數定義時的詞法作用域和相關變量。

三、閉包的用途

1.封裝私有變量
閉包可以用于封裝私有變量,以防止其被外部訪問和修改。封裝私有變量可以一定程度上防止全局變量污染,使用閉包封裝私有變量可以將這些變量限制在函數內部或模塊內部,從而減少了全局變量的數量,降低了全局變量被誤用或意外修改的風險。

在下面這個例子中,調用函數,輸出的結果都是1,但是顯然我們的代碼效果是想讓count每次加一的。

function add() {let count = 0;count++;console.log(count);
}
add()   //輸出1
add()   //輸出1
add()   //輸出1

一種顯而易見的方法是將count提到函數體外,作為全局變量。這么做當然是可以解決問題,但是在實際開發中,一個項目由多人共同開發,你不清楚別人定義的變量名稱是什么,這么做有點冒險,有什么其他的辦法可以解決這個問題呢?

function add(){let count = 0function a(){count++console.log(count);}return a
}
var res = add() 
res() //1 
res() //2
res() //3

答案是用閉包。在上面的代碼示例中,add函數返回了一個閉包a,其中包含了count變量。由于count只在add函數內部定義,因此外部無法直接訪問它。但是,由于a函數引用了count變量,因此count變量的值可以在閉包內部被修改和訪問。這種方式可以用于封裝一些私有的數據和邏輯。

2. 做緩存
函數一旦被執行完畢,其內存就會被銷毀,而閉包的存在,就可以保有內部環境的作用域。

function foo(){var myName ='張三'let test1 = 1const test2 = 2 var innerBar={getName: function(){console.log(test1);return myName},setName:function(newName){myName = newName}}return innerBar
}
var bar = foo()   
console.log(bar.getName()); //輸出:1 張三
bar.setName('李四')
console.log(bar.getName()); //輸出:1 李四

這里var bar = foo() 執行完后本來應該被銷毀,但是因為形成了閉包,所以導致foo執行上下文沒有被銷毀干凈,被引用了的變量myName、test1沒被銷毀,閉包里存放的就是變量myName、test1,這個閉包就像是setName、getName的專屬背包,setName、getName依然可以使用foo執行上下文中的test1和myName。

3. 模塊化編程(實現共有變量)

閉包還可以用于實現模塊化編程。模塊化編程是一種將程序拆分成小的、獨立的、可重用的模塊的編程風格。閉包可以用于封裝模塊的私有變量和方法,以便防止其被外部訪問和修改。例如:

const myModule = (function() {let privateVariable = '我是私有的!';

function privateMethod() {
console.log(privateVariable);
}

return {
publicMethod: function() {
privateMethod();
}
};
})();

myModule.publicMethod(); // 輸出: 我是私有的!

在上面的代碼示例中,myModule實際上是一個立即執行的匿名函數,它返回了一個包含publicMethod的對象。在函數內部,定義了一個私有變量privateVariable和一個私有方法privateMethod。publicMethod是一個公共方法,它可以訪問privateMethod,但是無法訪問privateVariable。這種方式可以用于實現簡單的模塊化編程。

四、閉包的缺點

閉包也存在著一個潛在的問題,由于閉包會引用外部函數的變量,但是這些變量在外部函數執行完畢后沒有被釋放,那么這些變量會一直存在于內存中,總的內存大小不變,但是可用內存空間變小了。 一旦形成閉包,只有在頁面關閉后,閉包占用的內存才會被回收,這就造成了所謂的內存泄漏。

因此我們在使用閉包時需要特別注意內存泄漏的問題,可以用以下兩種方法解決內存泄露問題:

1.及時釋放閉包:手動調用閉包函數,并將其返回值賦值為null,這樣可以讓閉包中的變量及時被垃圾回收器回收。
2.使用立即執行函數:在創建閉包時,將需要保留的變量傳遞給一個立即執行函數,并將這些變量作為參數傳遞給閉包函數,這樣可以保留所需的變量,而不會導致其他變量的內存泄漏。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/76216.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/76216.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/76216.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從小米汽車事故反思 LabVIEW 開發

近期&#xff0c;小米汽車的一起嚴重事故引發了社會各界的廣泛關注。這起事故不僅讓我們對智能汽車的安全性產生了深深的思考&#xff0c;也為 LabVIEW 開發領域帶來了諸多值得汲取的知識與領悟。 在智能汽車領域&#xff0c;尤其是涉及到智能駕駛輔助系統時&#xff0c;安全是…

項目進度延誤的十大原因及應對方案

項目進度延誤主要源于以下十大原因&#xff1a;目標不明確、需求頻繁變更、資源配置不足或不合理、溝通不暢、風險管理不足、缺乏有效的項目監控、技術難題未及時解決、團隊協作效率低下、決策鏈過長、外部因素影響。其中&#xff0c;需求頻繁變更是導致延誤的關鍵因素之一&…

AI 賦能 DBA:如何用 DeepSeek 等大模型簡化數據庫管理工作

AI 賦能 DBA:如何用 DeepSeek 等大模型簡化數據庫管理工作 摘要: 數據庫管理員(DBA)的工作涉及 SQL 優化、故障排查、性能監控等復雜任務。而 DeepSeek、ChatGPT 等大模型可以大幅減少重復勞動,提高 DBA 的工作效率。本文將結合真實案例,介紹如何利用 AI 優化 DBA 工作流…

vxe-table4.6 + vue3.2 + ant-design-vue 3.x 實現對列的顯示、隱藏、排序

概要 vxe-table中的vxe-toolbar沒有拖拽功能&#xff0c;故自己實現 源碼 <template><a-popover v-model:visible"open" placement"bottomRight" trigger"click"><template #content><div class"content">…

c++基礎知識二

1.面向對象 1.1 定義 面向對象編程是一種程序設計方法,它將數據和操作數據的方法封裝在一起,形成類。類是一種用戶自定義的數據類型,它包含了數據和對數據的操作方法。面向對象編程的特點包括封裝、繼承、多態 1.2 訪問控制符 public 公有屬性,方法。都可以訪問 prot…

Netty之ChannelOutboundBuffer詳解與實戰

深入理解Netty的高低水位線機制及其應用實踐 在高性能網絡編程中&#xff0c;Netty作為一個廣泛使用的異步事件驅動的Java框架&#xff0c;其高效的流量控制機制對于系統的穩定性和性能至關重要。本文將深入探討Netty中的高低水位線&#xff08;High/Low Water Mark&#xff0…

(自用)WebSocket創建流程

在Spring Boot項目中新建WebSocket服務&#xff0c;可以按照以下詳細步驟進行操作&#xff1a; 1.創建Spring Boot項目 可以通過Spring Initializr&#xff08;<>&#xff09;快速創建一個新的Spring Boot項目&#xff0c;添加Spring Web和Spring Boot DevTools依賴&…

JQuery初步學習

文章目錄 一、前言二、概述2.1 介紹2.2 安裝 三、語法3.1 文檔就緒3.2 選擇器 四、事件4.1 概述4.2 事件綁定/解綁4.3 一次性事件4.4 事件委托4.5 自定義事件 五、效果5.1 隱藏/顯示5.2 淡入淡出5.3 滑動5.4 動畫 六、鏈七、HTML7.1 內容/屬性7.2 元素操作7.3 類屬性7.4 樣式屬…

module錯誤集合

Library projects cannot set applicationId. applicationId is set to com.example.mylogin in default 在導入一個項目時&#xff0c;提示“Error:Library projects cannot set applicationId. applicationId is set to ‘com.xxx.yyy’ in default config.”&#xff0c;顯…

Spring Cloud 通用相關組件詳解

前言 Spring Cloud 是一個基于 Spring Boot 的微服務開發框架&#xff0c;它為開發者提供了一套完整的工具和組件&#xff0c;用于快速構建分布式系統中的常見模式&#xff08;如服務注冊與發現、負載均衡、配置管理等&#xff09;。本文將詳細介紹 Spring Cloud 的通用組件&a…

BUUCTF-web刷題篇(19)

28.CheckIn 源碼&#xff1a; #index.php <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv&q…

如何在Android系統上單編ko?

文章目錄 一、先了解編譯驅動需要什么&#xff1f;二、配置makefile1、在Android系統編譯LOG上找到編譯器信息&#xff08;一般都會打印出來&#xff09;2、基于源MK構造 可獨立運行的makefile3&#xff09;進入docker&#xff0c;在此makefile目錄下敲make4&#xff09;最后根…

【Pandas】pandas DataFrame to_numpy

Pandas2.2 DataFrame Conversion 方法描述DataFrame.astype(dtype[, copy, errors])用于將 DataFrame 中的數據轉換為指定的數據類型DataFrame.convert_dtypes([infer_objects, …])用于將 DataFrame 中的數據類型轉換為更合適的類型DataFrame.infer_objects([copy])用于嘗試…

2025常用的ETL 產品推薦:助力企業激活數據價值

在當今數字化時代&#xff0c;企業面臨著海量數據的挑戰與機遇&#xff0c;ETL&#xff08;Extract, Transform, Load&#xff09;工具作為數據整合與分析的關鍵環節&#xff0c;其重要性日益凸顯。ETL 廠商眾多&#xff0c;各有優勢&#xff0c;本文將從多個維度進行分析&…

LeetCode算法題(Go語言實現)_37

題目 給你一棵以 root 為根的二叉樹&#xff0c;二叉樹中的交錯路徑定義如下&#xff1a; 選擇二叉樹中 任意 節點和一個方向&#xff08;左或者右&#xff09;。 如果前進方向為右&#xff0c;那么移動到當前節點的的右子節點&#xff0c;否則移動到它的左子節點。 改變前進方…

博途 TIA Portal之1200做從站與匯川EASY的TCP通訊

上篇我們寫到了博途做主站與匯川EASY的通訊。通訊操作起來很簡單,當然所謂的簡單,也是相對的,如果操作成功一次,那么后面就很容易了, 如果操作不成功,就會很遭心。本篇我們將1200做從站,與匯川EASY做主站進行TCP的通訊。 1、硬件準備 1200PLC一臺,帶調試助手的PC機一…

Mysql(繼續更新)

INnoDB 三特性 事務 外鍵 行級鎖(開啟事務時,查詢后加FOR UPDATE) MySQL 使用 InnoDB&#xff0c;在 默認隔離級別 —— REPEATABLE READ&#xff08;可重復讀&#xff09; 下 開啟事務&#xff0c;執行 UPDATE 時默認會加行鎖 只要事務沒有提交 這條數據會鎖住 …

[IOI 1994] 數字三角形 Number Triangles

題目鏈接 思路&#xff08;上到下&#xff09;&#xff1a; ①從上往下遞推&#xff1a; f[i][j] max(f[i-1][j] g[i][j], f[i-1][j-1]g[i][j]) ②對最后一層&#xff0c;遍歷一下&#xff0c;找到最大的答案。 代碼&#xff08;上到下&#xff09;&#xff1a; #inclu…

基于Qt的串口通信工具

程序介紹 該程序是一個基于Qt的串口通信工具&#xff0c;專用于ESP8266 WiFi模塊的AT指令配置與調試。主要功能包括&#xff1a; 1. 核心功能 串口通信&#xff1a;支持串口開關、參數配置&#xff08;波特率、數據位、停止位、校驗位&#xff09;及數據收發。 AT指令操作&a…

第5篇:Linux程序訪問控制FPGA端LEDR<三>

Q&#xff1a;如何具體設計.c程序代碼訪問控制FPGA端外設&#xff1f; A&#xff1a;以控制DE1-SoC開發板的LEDR為例的Linux .C程序代碼。頭文件fcntl.h和sys/mman.h用于使用/dev/mem文件&#xff0c;以及mmap和munmap內核函數&#xff1b;address_map_arm.h指定了DE1-SoC_Com…