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

基本數(shù)據(jù)類型

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

導圖:














js內存

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

js中的內存為兩種-----棧和堆。
基本數(shù)據(jù)類型存儲

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









下面解釋為什么當a的值變?yōu)?24時,b為什么不改變






















可以看出a和b的值相互獨立,當代碼執(zhí)行到a++時,只是a的值變?yōu)?24,而b也是123
引用數(shù)據(jù)類型存儲:

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

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

舉例說明:














當obj屬性name變?yōu)?孫悟空"時,obj1屬性name也變?yōu)?孫悟空"

內存分析——解釋上述現(xiàn)象




















當棧存放引用類型時,值為對象的地址,obj與obj1指向同一個地址,所以當obj的name值變?yōu)椤皩O悟空”時,obj1也會發(fā)生變化

看完這個,相信小伙伴們已經很清晰的明白,為什么會有淺拷貝和深拷貝這兩兄弟了吧。那么這兩個到底有什么區(qū)別尼?別著急,我們一步一步道來。
深拷貝和淺拷貝的區(qū)別

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










淺拷貝只復制指向某個對象的指針,而不復制對象本身,新舊對象還是共享同一塊內存。但深拷貝會另外創(chuàng)造一個一模一樣的對象,新對象跟原對象不共享內存,修改新對象不會改到原對象。
賦值和淺拷貝的區(qū)別

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

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






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


























































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










淺拷貝的實現(xiàn)方法

    1、Object.assign()

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

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

深拷貝的實現(xiàn)方法

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

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

    2、自己遞歸實現(xiàn)一個簡單深拷貝

深拷貝,主要用到的思想是遞歸,遍歷對象、數(shù)組直到里邊都是基本數(shù)據(jù)類型,然后再去復制,就是深度拷貝。

示例代碼:

//定義檢測數(shù)據(jù)類型的功能函數(shù)
    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、第三方深拷貝庫
 


歡迎關注微信公眾號:猴哥說前端