自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

閉包是如何產(chǎn)生的?你知道嗎?

開發(fā) 前端
調(diào)用函數(shù)時(shí),會(huì)產(chǎn)生調(diào)用棧,將當(dāng)前函數(shù)上下文入棧,會(huì)保存基本類型變量。引用變量會(huì)在堆內(nèi)存中創(chuàng)建,然后在棧內(nèi)存中引用過來。

大家好,我是前端西瓜哥。

這次從內(nèi)存管理的角度來看看,閉包是怎么產(chǎn)生的。

我們知道,在調(diào)用函數(shù)時(shí),其實(shí)會(huì)產(chǎn)生臨時(shí)的 調(diào)用棧。這些調(diào)用棧保存的是 執(zhí)行上下本,并實(shí)際保存在 棧內(nèi)存 中。

每執(zhí)行一個(gè)函數(shù),函數(shù)內(nèi)的局部臨時(shí)變量會(huì)臨時(shí)保存起來。如果此時(shí)函數(shù)又調(diào)用了另一個(gè)函數(shù),另一個(gè)函數(shù)下的局部變量也要保存下來,就這樣,我們產(chǎn)生了棧。

當(dāng)一個(gè)函數(shù)執(zhí)行完后,它對(duì)應(yīng)的局部臨時(shí)變量就會(huì)被銷毀。

局部變量保存下來,是為了保護(hù)上下文現(xiàn)場(chǎng)。

舉例說明一下:

function a() {
const a_num = 99;
const a_obj = { val: "a" };
b();
}

function b() {
const b_str = "text";
c();
}

function c() {
const c_bool = true;
// debugger
}

a();

這里我們嵌套調(diào)用了 a、b、c 函數(shù),會(huì)產(chǎn)生如下的調(diào)用棧。

圖片

基本類型的臨時(shí)變量,會(huì)直接保存到棧內(nèi)存中,對(duì)于引用類型,則是在堆內(nèi)存中生成,然后將地址拿到,保存到棧內(nèi)存中。

引用類型為什么不直接放到棧內(nèi)存中?因?yàn)闂?nèi)存不是很大,很容易就棧溢出,而引用類型通常很大。

閉包的產(chǎn)生

函數(shù)調(diào)用完成后,它內(nèi)部聲明的臨時(shí)變量會(huì)被銷毀。理論上應(yīng)該如此,但如果使用了閉包,可以會(huì)讓臨時(shí)變量一直保留不被銷毀。

例子:

function createCounter() {
let count = 0;
let otherVal = "other val";
return function counter() {
// debugger;
console.log(count++);
};
}

const counter = createCounter();

console.log(counter());

執(zhí)行過程為:

  1. 執(zhí)行函數(shù) createCounter 時(shí),會(huì)創(chuàng)建一個(gè)空的上下文對(duì)象。
  2. 遇到內(nèi)部函數(shù) counter,會(huì)預(yù)掃描內(nèi)部函數(shù) counter 使用了 createCounter 下的哪些便利,最終掃描出 count 變量。于是在堆內(nèi)存創(chuàng)建一個(gè)閉包 Closure (createCounter) 對(duì)象,將 count 加進(jìn)去。otherVal 不會(huì)加到閉包對(duì)象上,因?yàn)樗鼪]有被使用。
  3. 這個(gè)內(nèi)部函數(shù)最后被返回,被引用,閉包就一直不會(huì)銷毀。

使用 DevTool 可以觀察到這個(gè)閉包對(duì)象:

圖片

所以,如果一個(gè)閉包返回的函數(shù)執(zhí)行完后不用了,要設(shè)置為 null。否則它關(guān)聯(lián)的閉包對(duì)象會(huì)一直在那里占用內(nèi)存。

多個(gè)內(nèi)部函數(shù)共享一個(gè)閉包對(duì)象

另外,如果有多個(gè)內(nèi)部函數(shù),這些函數(shù)會(huì)共用同一個(gè)閉包對(duì)象。即使其中的一個(gè)內(nèi)部函數(shù)不會(huì)返回,它也會(huì)給閉包對(duì)象加?xùn)|西。

下面我們加了一個(gè) printOtherVal 的內(nèi)部函數(shù),它并不返回,但還是會(huì)導(dǎo)致返回 counter 函數(shù)對(duì)應(yīng)的閉包對(duì)象帶上了它不需要的 otherVal 變量。

圖片

這是 JS 引擎處理閉包策略問題,理論不應(yīng)該有這樣奇怪的效果。

結(jié)尾

調(diào)用函數(shù)時(shí),會(huì)產(chǎn)生調(diào)用棧,將當(dāng)前函數(shù)上下文入棧,會(huì)保存基本類型變量。引用變量會(huì)在堆內(nèi)存中創(chuàng)建,然后在棧內(nèi)存中引用過來。

因?yàn)?JavaScript 中函數(shù)是第一公民,所以會(huì)有閉包的概念。當(dāng)發(fā)現(xiàn)內(nèi)部函數(shù),會(huì)創(chuàng)建一個(gè)閉包對(duì)象,將其中使用到的外部函數(shù)變量保存到該閉包對(duì)象下。之后內(nèi)部函數(shù)被調(diào)用時(shí),就會(huì)從閉包里提取變量,如果找不到則從全局上下文提取。

責(zé)任編輯:姜華 來源: 前端西瓜哥
相關(guān)推薦

2024-11-26 00:45:29

free區(qū)域字段

2024-06-20 08:06:30

2024-10-05 00:00:00

HTTPS性能HTTP/2

2025-01-16 16:41:00

ObjectConditionJDK

2024-10-24 08:47:12

2023-12-20 08:23:53

NIO組件非阻塞

2023-05-30 08:19:07

kafka集群leader

2024-12-04 08:40:19

2024-02-19 00:00:00

Docker輕量級(jí)容器

2022-11-28 00:04:17

2024-01-15 12:16:37

2024-07-30 08:22:47

API前端網(wǎng)關(guān)

2024-11-08 09:48:38

異步編程I/O密集

2023-02-28 07:39:18

2024-03-19 08:01:54

服務(wù)熔斷軟件設(shè)計(jì)模式微服務(wù)

2024-02-19 07:44:52

虛擬機(jī)Java平臺(tái)

2023-03-21 07:39:51

CentOS掛載硬盤

2023-01-13 17:02:10

操作系統(tǒng)鴻蒙

2024-07-08 00:00:01

多線程ThreadC#

2024-06-27 10:51:28

生成式AI領(lǐng)域
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)