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

面試官:講一下閉包?內(nèi)存泄露場景?循環(huán)引用為什么導(dǎo)致內(nèi)存泄露?怎么判斷是否存在循環(huán)引用?

開發(fā) 前端
這篇文章就跟大家詳細(xì)的說一下關(guān)于閉包的問題,爭取可以做到讓大家看完這篇文章之后,對(duì)比閉包的問題可以順暢回答!

Hello,大家好,我是 Sunday。

在最近的中小廠面試中,【閉包】的問題被很多公司提到。如果單純說閉包是比較簡單的,一句話就可以說清楚:“可以訪問其他函數(shù)作用域中變量的函數(shù),就是閉包函數(shù)”。

但是,隨后延伸的問題,如:閉包造成內(nèi)存泄漏的場景、循環(huán)引用為什么導(dǎo)致內(nèi)存泄露?怎么判斷是否存在循環(huán)引用? 等問題,很多同學(xué)回答的并不好。

因此,這篇文章就跟大家詳細(xì)的說一下關(guān)于閉包的問題,爭取可以做到讓大家看完這篇文章之后,對(duì)比閉包的問題可以順暢回答!

1. 什么是閉包

閉包是指 函數(shù)在創(chuàng)建時(shí)保留了對(duì)其定義作用域的引用,即使函數(shù)執(zhí)行在其詞法作用域之外,也能訪問該作用域中的變量。

閉包在 JavaScript 中的常見表現(xiàn)形式是:函數(shù)嵌套函數(shù),內(nèi)部函數(shù)訪問外部函數(shù)的變量。

由于 JavaScript 的函數(shù)是“第一類公民”,可以作為值返回、傳遞或保存,因此在外部函數(shù)返回后,閉包依然保留對(duì)外部變量的訪問權(quán)限。

function outerFunction() {
  let counter = 0;
  
  return function innerFunction() {
    counter++;
    console.log(counter);
  };
}

const increment = outerFunction();
increment(); // 輸出: 1
increment(); // 輸出: 2

在上述代碼中,innerFunction 是一個(gè)閉包,它可以訪問 outerFunction 中的變量 counter,即使 outerFunction 已經(jīng)執(zhí)行完畢。

2. 閉包導(dǎo)致的內(nèi)存泄露場景

在 JS 中,閉包有時(shí)會(huì)導(dǎo)致內(nèi)存泄露,這是因?yàn)?閉包在訪問外部作用域的變量時(shí)會(huì)讓這些變量無法被垃圾回收,從而導(dǎo)致不必要的內(nèi)存占用。

2.1. 常見的內(nèi)存泄露場景

  • 未清理的事件監(jiān)聽:如果事件監(jiān)聽器引用了外部作用域中的變量,且在不需要時(shí)未移除,則會(huì)導(dǎo)致閉包一直存在,無法釋放內(nèi)存。
function addEvent() {
  const element = document.getElementById('button');
  const someData = "Important data";
  element.addEventListener('click', function() {
    console.log(someData); // 閉包引用了外部變量 someData
  });
}

addEvent();
// 這里如果不手動(dòng)移除事件監(jiān)聽器,則 someData 永遠(yuǎn)不會(huì)被釋放,造成內(nèi)存泄露
  • 定時(shí)器未清理:在定時(shí)器的回調(diào)函數(shù)中使用了閉包,但在不再需要時(shí)未清除定時(shí)器,導(dǎo)致回調(diào)函數(shù)及其引用的外部變量無法被回收。
function createTimer() {
  const largeData = new Array(10000).fill('*');
  setInterval(function() {
    console.log(largeData); // 定時(shí)器閉包持有 largeData 的引用
  }, 1000);
}

createTimer();
// 這里如果不清除定時(shí)器,largeData 將永遠(yuǎn)無法釋放

3. 循環(huán)引用導(dǎo)致內(nèi)存泄露

循環(huán)引用是指:兩個(gè)或多個(gè)對(duì)象相互引用,從而形成一個(gè)循環(huán)結(jié)構(gòu),導(dǎo)致垃圾回收器無法回收這些對(duì)象。

3.1. 為什么循環(huán)引用會(huì)導(dǎo)致內(nèi)存泄露?

JS 的垃圾回收機(jī)制使用 標(biāo)記清除(mark-and-sweep) 算法。即:垃圾回收器會(huì)從根對(duì)象(如全局對(duì)象)出發(fā),查找所有可達(dá)對(duì)象。

若對(duì)象形成了循環(huán)引用,且不再被根對(duì)象訪問,則垃圾回收器無法將其清除,這會(huì)導(dǎo)致這些對(duì)象長期保留在內(nèi)存中,形成內(nèi)存泄露。

function createCircularReference() {
  const objectA = {};
  const objectB = {};
  objectA.ref = objectB; // objectA 引用 objectB
  objectB.ref = objectA; // objectB 引用 objectA,形成循環(huán)引用
}

createCircularReference();
// 這里 objectA 和 objectB 都無法被回收

在這個(gè)示例中,objectA 和 objectB 互相引用,形成了循環(huán)引用。如果沒有外部引用它們,按理說可以被垃圾回收,但由于相互持有的引用,導(dǎo)致它們無法被清除,形成內(nèi)存泄露。

4. 如何檢測(cè)循環(huán)引用

在項(xiàng)目中,如果出現(xiàn) 內(nèi)存泄漏 的問題,那么可以通過以下方式進(jìn)行檢查:

  • 手動(dòng)檢測(cè):在代碼中通過邏輯分析或使用 console.log 輸出檢查對(duì)象的相互引用關(guān)系。
  • 使用開發(fā)者工具檢測(cè):現(xiàn)代瀏覽器的開發(fā)者工具提供了內(nèi)存快照和堆分析,可以捕獲內(nèi)存快照來分析內(nèi)存的使用情況,幫助發(fā)現(xiàn)循環(huán)引用和內(nèi)存泄露。在 Chrome 開發(fā)者工具中,可以通過 Memory(內(nèi)存) 面板,使用 Heap Snapshot(堆快照)來查看對(duì)象的引用關(guān)系,并檢查是否有意外的循環(huán)引用。

圖片圖片

  • JSON.stringify 檢測(cè):嘗試使用 JSON.stringify 序列化對(duì)象,如果對(duì)象中存在循環(huán)引用,JSON.stringify 會(huì)拋出 TypeError 異常,可以用這種方式簡單檢測(cè)循環(huán)引用(注意這種方法只能用于檢測(cè)較簡單的循環(huán)引用,復(fù)雜場景需結(jié)合其他方法)。
function hasCircularReference(obj) {
  try {
    JSON.stringify(obj);
    return false; // 無循環(huán)引用
  } catch (error) {
    return true; // 有循環(huán)引用
  }
}

const objectA = {};
const objectB = { ref: objectA };
objectA.ref = objectB;

console.log(hasCircularReference(objectA)); // 輸出: true
  1. WeakMap 弱引用:使用 WeakMap 結(jié)構(gòu)管理對(duì)象引用。由于 WeakMap 的鍵是弱引用,不會(huì)影響對(duì)象的垃圾回收,可以通過 WeakMap 追蹤對(duì)象引用關(guān)系,并避免循環(huán)引用導(dǎo)致的內(nèi)存泄露。

5. 如何避免循環(huán)引用導(dǎo)致的內(nèi)存泄露

如果檢測(cè)出現(xiàn)內(nèi)存泄漏的問題,那么可以通過以下方式嘗試解決:

  • 避免對(duì)象互相引用:在設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)時(shí),盡量避免互相引用,尤其是大的復(fù)雜對(duì)象。
  • 使用 WeakMap 或 WeakSet:在 JavaScript 中,WeakMap 和 WeakSet 是弱引用結(jié)構(gòu),存儲(chǔ)在 WeakMap 或 WeakSet 中的對(duì)象不會(huì)被阻止垃圾回收??梢允褂?WeakMap 和 WeakSet 來存儲(chǔ)對(duì)象之間的引用關(guān)系,避免循環(huán)引用導(dǎo)致的內(nèi)存泄露。
const weakMap = new WeakMap();
const objectA = {};
const objectB = {};

weakMap.set(objectA, objectB);
  • 在不需要時(shí)手動(dòng)斷開引用:當(dāng)對(duì)象不再使用時(shí),可以手動(dòng)將引用設(shè)為 null 或 undefined,確保垃圾回收器能夠正?;厥账鼈?。
let objectA = {};
let objectB = {};
objectA.ref = objectB;
objectB.ref = objectA;

// 當(dāng)不再需要時(shí),斷開引用關(guān)系
objectA.ref = null;
objectB.ref = null;


責(zé)任編輯:武曉燕 來源: 程序員Sunday
相關(guān)推薦

2017-11-15 19:30:08

Python內(nèi)存泄露循環(huán)引用

2017-12-11 11:00:27

內(nèi)存泄露判斷

2016-09-08 16:16:26

iOS移動(dòng)應(yīng)用內(nèi)存泄漏

2022-06-07 12:03:33

Java內(nèi)存模型

2021-12-20 10:30:33

forforEach前端

2021-10-27 07:15:36

Go 循環(huán)引用

2024-03-13 07:53:57

弱引用線程工具

2024-02-22 15:36:23

Java內(nèi)存模型線程

2020-08-07 15:15:01

Java內(nèi)存泄漏面試

2025-03-10 07:05:07

2022-10-18 08:38:16

內(nèi)存泄漏線程

2025-02-27 00:08:24

2021-05-27 21:47:12

Python垃圾回收

2023-09-12 14:56:13

MyBatis緩存機(jī)制

2021-04-19 18:56:58

大數(shù)字符串運(yùn)算

2021-07-28 10:08:19

類加載代碼塊面試

2017-05-04 16:07:11

Tomcat內(nèi)存泄露

2023-11-15 09:14:27

Java值傳遞

2010-08-10 10:00:57

Flex內(nèi)存

2013-04-09 14:49:18

Linux內(nèi)存統(tǒng)計(jì)內(nèi)存泄露
點(diǎn)贊
收藏

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