深入探討深拷貝淺拷貝兩兄弟

基本數據類型

在深入探討深拷貝和淺拷貝之前,我們需要先了解一下Javascript得數據類型。衆所周知JavaScript得數據類型,分爲基本數據類型和引用數據類型。那麼這兩種類型到底有什麼區別?接下來我們詳細的談談。

導圖:


js內存

接下來我們還需要了解一個重要的知識點----js中的內存

js中的內存爲兩種-----棧和堆。


基本數據類型存儲

基本數據類型的值存在棧裏面,並且值與值之間獨立存在,修改一個值,不會影響其他的值。舉個列子:把a的值傳給b

下面解釋爲什麼當a的值變爲124時,b爲什麼不改變

可以看出a和b的值相互獨立,當代碼執行到a++時,只是a的值變爲124,而b也是123


引用數據類型存儲:

  • 對象保存在堆內存中
  • 每創建一個新的對象就會在堆內存開闢一個新的空間
  • 變量保存的是內存地址(對象引用)
  • 兩個變量保存同一個引用,一個變量修改屬性時,另一個變量屬性值也會變化

引用數據類型在棧中存儲了指針,該指針指向堆中該實體的起始地址。當解釋器尋找引用值時,會首先檢索其在棧中的地址,取得地址後從堆中獲得實體。

舉例說明:

當obj屬性name變爲"孫悟空"時,obj1屬性name也變爲"孫悟空"

內存分析——解釋上述現象

當棧存放引用類型時,值爲對象的地址,obj與obj1指向同一個地址,所以當obj的name值變爲“孫悟空”時,obj1也會發生變化


看完這個,相信小夥伴們已經很清晰的明白,爲什麼會有淺拷貝和深拷貝這兩兄弟了吧。那麼這兩個到底有什麼區別尼?彆着急,我們一步一步道來。


深拷貝和淺拷貝的區別

深拷貝和淺拷貝是隻針對Object和Array這樣的引用數據類型的。示意圖大致如下:

淺拷貝只複製指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。


賦值和淺拷貝的區別

當我們把一個對象賦值給一個新的變量時,賦的其實是該對象的在棧中的地址,而不是堆中的數據。也就是兩個對象指向的是同一個存儲空間,無論哪個對象發生改變,其實都是改變的存儲空間的內容,因此,兩個對象是聯動的。

淺拷貝是按位拷貝對象,它會創建一個新對象,這個對象有着原始對象屬性值的一份精確拷貝。如果屬性是基本類型,拷貝的就是基本類型的值;如果屬性是內存地址(引用類型),拷貝的就是內存地址 ,因此如果其中一個對象改變了這個地址,就會影響到另一個對象。即默認拷貝構造函數只是對對象進行淺拷貝複製(逐個成員依次拷貝),即只複製對象空間而不復制資源。

我們先來看兩個例子,對比賦值與淺拷貝會對原對象帶來哪些改變?




上面例子中,obj1是原始數據,obj2是賦值操作得到,而obj3淺拷貝得到。我們可以很清晰看到對原始數據的影響,具體請看下錶:


淺拷貝的實現方法

  • 1、Object.assign()

Object.assign()方法可以將任意多個的源對象自身的可枚舉屬性拷貝給目標對象,然後返回目標對象。但是該方法拷貝的時候是淺拷貝,拷貝的是對象的屬性的引用,並不是對象本身。(注意:當object只有一層的時候,就是淺拷貝)

  • 2、Array.prototype.concat()
  • 3、Array.prototype.slice()

深拷貝的實現方法

  • 1、JSON.parse(JSON.stringify())

JSON.stringify()是前端開發過程中比較常用的深拷貝方式。原理是把一個對象序列化成爲一個JSON字符串,將對象的內容轉換成字符串的形式再保存在磁盤上,再用JSON.parse()反序列化將JSON字符串變成一個新的對象。

  • 2、自己遞歸實現一個簡單深拷貝

深拷貝,主要用到的思想是遞歸,遍歷對象、數組直到裏邊都是基本數據類型,然後再去複製,就是深度拷貝。

示例代碼:

//定義檢測數據類型的功能函數
    function isObject(obj) {
	    return typeof obj === 'object' && obj != null;
    }
   function cloneDeep(source) {

    if (!isObject(source)) return source; // 非對象返回自身
      
    var target = Array.isArray(source) ? [] : {};
    for(var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (isObject(source[key])) {
                target[key] = cloneDeep(source[key]); // 注意這裏
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}
  • 3、第三方深拷貝庫

參考

  • https://segmentfault.com/a/1190000018874254

歡迎大家關注我的公衆號,一起交流進步
在這裏插入圖片描述

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