一張紙搞懂JS系列(2)之JS記憶體生命週期,棧記憶體與堆記憶體,深淺拷貝

2020-09-29 18:00:51

寫在最前面:在欄目下這是我即將開始寫的一個系列,主要是在框架橫行的時代,雖然上班用的是框架,但是對於面試,以及技術進階,JS基礎知識的鋪墊是錦上添花,也是不得不學習的一塊知識,雖然開汽車的不需要很懂汽車,只需要掌握汽車的常用功能即可。但是如果你懂汽車,那你也能更好地開車,同理。當然,一篇文章也不會光光只講一個知識點,一般會將有關聯的知識點串聯起來,一邊記錄自己的學習,一邊分享自己的學習,互勉!如果可以的話,也請給我點個贊,你的點贊也能讓我更加努力地更新!

概覽

  • 食用時間: 6-12分鐘
  • 難度: 簡單,別跑,看完再走

JS記憶體生命週期

  • 分配記憶體

  • 記憶體的讀與寫

  • 釋放記憶體

棧記憶體與堆記憶體

JS資料型別

在講棧記憶體與堆記憶體之前,大家應該都知道JS分為兩種資料型別:

  • 基本資料型別

    String , Number , Boolean , null , undefined , Symbol (大小固定,體積輕量,相對簡單)

  • 參照資料型別

    Object , Array , Function (大小不一定,佔用空間較大,相對複雜)

記憶體儲存機制

var a=true;      //布林型,基本資料型別var b='jack';    //字元型,基本資料型別var c=18;        //數值型,基本資料型別var d={name:'jack'};   //物件,參照資料型別var d=[0,1,2,3,4,5];   //陣列,參照資料型別複製程式碼

正是因為資料型別的不同,所以他們的存放方式也不同,就和現實生活中窮人和富人的住所完全不一樣(扯遠了)。我們先來看一張圖:

可以看到, a , b , c 都是基本資料型別, de 都是參照資料型別,他們在存放方式上有著本質性的區別,基本資料型別的值是存放在棧記憶體中的,而參照資料型別的值是存放在堆記憶體中的,棧記憶體中僅僅存放著它在棧記憶體中的參照(即它在堆記憶體中的地址),就和它的名字一樣,參照資料型別

記憶體存取機制

上面講的是儲存,接下來說一下變數的存取,基本資料型別可以直接從棧記憶體中存取變數的值,而參照資料型別要先從棧記憶體中找到它對應的參照地址,再拿著這個參照地址,去堆記憶體中查詢,才能拿到變數的值

深淺拷貝

  • 淺拷貝

    上面已經和大家說過了基本資料型別與參照資料型別在儲存上的不同,那麼,接下來說的這個深淺拷貝,想必大家也在面試題中經常碰到,老方式,先來看一段程式碼

    var name='jack';var obj={  age:24};var nameCopy=name;var objCopy=obj;
    
    nameCopy='bob';
    objCopy.age=15;console.log(name);    //jackconsole.log(obj.age);     //15複製程式碼

    你會發現, name 是沒有被影響的,而我們命名是修改objCopy.age,為什麼還會影響到 obj.age呢,這就是因為深淺拷貝的問題在搗鬼,先來看下下面的一張圖

之所以會出現這種情況,是因為JS對於基本型別和參照型別的,當我們在複製參照型別的時候,複製的是該物件的參照地址,所以,在執行 var objCopy=obj;的時候,將 obj參照地址複製給了 objCopy,所以,這兩個物件實際指向的是同一個物件,即改變 objCopy 的同時也改變了 obj 的值,我們將這種情況稱為淺拷貝,僅僅複製了物件的參照,並沒有開闢新的記憶體,拿人手短,拷貝地太淺了。(只有參照型別才會出現淺拷貝的情況)

  • 深拷貝

    再來看接下來的一段程式碼

    var name='jack';var obj={  age:24};var nameCopy=name;var objCopy=JSON.parse(JSON.stringify(obj));
    
    nameCopy='bob';
    objCopy.age=15;console.log(name);    //jackconsole.log(obj.age);     //24複製程式碼

    可以發現,在經過 JSON.parse(JSON.stringify(obj)) 轉換了以後,淺拷貝不復存在,這一波是深拷貝,深拷貝開闢了新的堆記憶體地址,並且將物件的參照指向了新開闢的記憶體地址,和前面複製的物件完全獨立,自立根生,拷貝地很深,學功夫學到家,自立門戶的感覺。

  • 另外實現深拷貝的方法(更多方式請自行百度)

    var objCopy=Object.assign({},obj);   //物件深拷貝的方法 Object.assign
    var arrayCopy=array.concat();       //陣列深拷貝的方法  concat()  (陣列無巢狀物件或者陣列可用)
    var arrayCopy=array.slice();       //陣列深拷貝的方法  slice()   (陣列無巢狀物件或者陣列可用)
    JSON.parse(JSON.stringify(array))     //順帶提下,JSON.parse(JSON.stringify())   陣列和物件通用複製程式碼

    接著上面的陣列容易踩坑的地方 ,來看一個例子

    var array = [{name: 'jack'}, ['old']];var arrCopy = array.concat();
    arrCopy[0].name='new';console.log(array); // [{name: 'new'}, ['old']]console.log(arrCopy); // [{name: 'new'}, ['old']]複製程式碼

    可以清楚地看到(陣列無巢狀物件或者陣列可用的情況下用 concatslice 才有效)

系列目錄

  • 一張紙搞懂JS系列(2)之JS記憶體生命週期,棧記憶體與堆記憶體,深淺拷貝

  • 一張紙搞懂JS系列(3)之垃圾回收機制,記憶體漏失,閉包

更多相關免費學習推薦:(視訊)

以上就是一張紙搞懂JS系列(2)之JS記憶體生命週期,棧記憶體與堆記憶體,深淺拷貝的詳細內容,更多請關注TW511.COM其它相關文章!