js難點彙總01_內存

正所謂底層基礎決定上層建築,如果想寫出優雅高性能的前端應用,就必須瞭解JS的內存機制。他會幫助你理解譬如:閉包、深淺複製、引用數據類型和引用傳遞;

堆比棧大,棧比堆的運算速度快,對象是一個複雜的結構,並且可以自由擴展,如:數組可以無限擴充,對象可以自由添加屬性。將他們放在堆中是爲了不影響棧的效率。而是通過引用的方式查找到堆中的實際對象再進行操作。相對於簡單數據類型而言,簡單數據類型就比較穩定,並且它只佔據很小的內存。不將簡單數據類型放在堆是因爲通過引用到堆中查找實際對象是要花費時間的,而這個綜合成本遠大於直接從棧中取得實際值的成本。所以簡單數據類型的值直接存放在棧中。

記住一句話:能量是守衡的,無非是時間換空間,空間換時間的問題

JS內存週期

內存分配、內存使用、內存回收

JS類型

談到js內存,我們從數據類型來作爲切入口幫助理解,js數據類型分兩大類,分別是基本數據類型引用數據類型,基本數據類型又包含:null、undefined、boolean、number、string,引用數據類型是複合數據類型:Object、Function、Array等

JS內存模型

JS內存空間分爲棧(stack)、堆(heap)、池(一般放在棧中),棧存放變量(基本數據類型),堆存放複雜對象(醫用數據類型),池存放常量

1. 基礎數據類型與棧內存

JS中的基礎數據類型都有固定的大小,往往都放在棧內存中,由系統自動分配內存空間;可以直接操作棧內存空間的值,遵循先進後出的原則,如下圖所示

2. 引用數據類型與堆內存

引用數據類型例如Object、array大小都是不固定的,是放在堆內存中的變量;JavaScript不允許直接訪問堆裏面的變量,所以不能直接操作堆內存空間,在操作對象中,我們操作的是對象的引用而不是實際的對象。因此,引用類型的值都是按引用訪問的,這裏的引用可以理解爲報錯在變量對象中的一個地址,這個地址和堆內存的實際值是相關聯的,這就是我們常說的指針也就是是我們去複製一個對象的時候只是複製了一個指針,或者說複製了一個引用地址,如下圖所示:

這就衍生了如何去複製一個對象,一般兩種方法,一種是轉化爲string(就是把引用類型轉化爲基本數據類型,這樣就可以直接複製值了),第二種是Object.assign(),通過Object.assign({},srcObj);得到的新對象爲‘深拷貝’;如果屬性值爲對象或其它引用類型,那對於這個對象而言其實是淺拷貝的。這是Object.assign()特別值得注意的地方。 

function deepClone2(obj) {
  let _obj = JSON.stringify(obj),
  return JSON.parse(_obj);
}
let srcObj = {
  'name': 'lilei',
  'age': '20'
};

let copyObj2 = Object.assign({}, srcObj, {
  'age': '21'
});

3. 內存空間管理

由於JavaScript有自動垃圾回收機制,所以很多人都不太在意內存使用問題,內存的分配和回收全都交給了自動管理;但是我們要做到知其然,知其所以然,這樣才能寫出高性能的優雅代碼;

JavaScript的內存週期補充:

1. 分配所需要的內存 

2. 使用分配的內存(讀寫)

3. 不需要的時候釋放、回收

let demo=20;//分配內存
alert(demo+20);//使用內存
let demo=null;//使用完畢,釋放內存

補充第三部回收,JavaScript有自動垃圾收集機制,那麼這個自動垃圾收集機制的原理是什麼呢?其實很簡單,就是找出那些不再繼續使用的值,然後釋放其佔用的內存。垃圾收集器會每隔固定的時間段就執行一次釋放操作。

在JavaScript中,最常用的是通過標記清除的算法來找到哪些對象是不再繼續使用的,因此a = null其實僅僅只是做了一個釋放引用的操作,讓 a 原本對應的值失去引用,脫離執行環境,這個值會在下一次垃圾收集器執行操作時被找到並釋放。而在適當的時候解除引用,是爲頁面獲得更好性能的一個重要方式。

補充:閉包中的變量存儲於堆內存還是棧內存的問題,請先看如下代碼快:

function demo(){
    let a=1;
    function children(){
        console.log(a)
    }
    return children
}

我先闡述下閉包定義:閉包就是能夠讀取其他函數內部變量的函數

代碼塊裏:函數demo返回了一個函數children,並且函數children使用了外部函數demo的變量a,函數children就被稱爲閉包;

我們來從堆棧來解釋以上代碼塊:函數demo中的變量是存儲在堆內存的,所以函數children可以訪問函數demo中的變量

4. 內存泄漏

內存泄漏可能對於前端開發着來說比較陌生,但是你肯定遇到過瀏覽器卡死的現象,卡死的原因就可能是因爲一個死循環導致的內存爆滿泄漏。所以對於持續運行的服務進程(daemon),必須及時釋放不再用到的內存。否則,內存佔用越來越高,輕則影響系統性能,重則導致進程崩潰。 對於不再用到的內存,沒有及時釋放,就叫做內存泄漏(memory leak)

如果想模擬的話,可以按下面的步驟來進行操作:

1. 打開開發者工具,選擇 Memory

2. 在右側的Select profiling type字段裏面勾選 timeline

3. 點擊左上角的錄製按鈕。

4. 在頁面上進行各種操作,模擬用戶的使用情況。

5. 一段時間後,點擊左上角的 stop 按鈕,面板上就會顯示這段時間的內存佔用情況。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章