前端值得一探究竟特輯 ---深拷貝與淺拷貝

1、深度理解深拷貝與淺拷貝

要立即深拷貝與淺拷貝,不得不從語言本身的角度出發,迴歸Javascript的數據類型。

原始類型

javascript中的原始值(undefined、null、布爾值、數字和字符串)與對象(包括數組和函數)有着根本區別。原始值是不可更改的:任何方法都無法更改(或“突變”)一個原始值,原始類型的比較是值的比較。

簡單說來就是:基本數據類型值不可變
對數字和布爾值來說顯然如此 —— 改變數字的值本身就說不通,而對字符串來說就不那麼明顯了,因爲字符串看起來像由字符組成的數組,我們期望可以通過指定索引來假改字符串中的字符。實際上,javascript 是禁止這樣做的。字符串中所有的方法看上去返回了一個修改後的字符串,實際上返回的是一個新的字符串值。

基本數據類型的賦值是傳值。在我們進行賦值操作的時候,基本數據類型的賦值(=)是在內存中新開闢一段棧內存,然後再把再將值賦值到新的棧中。

let str = 'abc';
str[0] // "a"
str[0] = 'A' // 返回了一個新字符串'A',並沒有改變str
str //"abc"

引用類型

引用類型(object)是存放在堆內存中的,變量實際上是一個存放在棧內存的指針,這個指針指向堆內存中的地址。引用類型值可變,引用類型的比較是引用的比較。

引用類型的賦值是傳址。只是改變指針的指向,例如,也就是說引用類型的賦值是對象保存在棧中的地址的賦值,這樣的話兩個變量就指向同一個對象,因此兩者之間操作互相有影響。

因此就出現了深拷貝和淺拷貝的現象。

直接複製、深拷貝與淺拷貝

直接複製: 新對象會指向源對象內存地址,源對象修改自身基本類型也會影響新對象。

淺度拷貝:新對象不會指向源對象內存地址,環境會爲新對象開闢新的堆內存,用於存儲新對象,新對象內的基本類型將通過傳值獲得,但對源對象裏的引用類型進行傳址操作,因此當改變源的對象裏面的引用類型時,新對象也會改變。

深度拷貝:新對象不會指向源對象內存地址,環境會爲新對象開闢新的堆內存,需要遞歸拷貝對象裏的引用,直到子屬性都爲基本類型。兩個對象對應兩個不同的地址,修改源對象子對象的屬性,不會影響新對象子對象的屬性。

2、手撕深度拷貝

1. 如何寫出一個驚豔面試官的深拷貝?

處理基本類型中的null, 以及引用對象的正則表達式對象

    function deepClone(sup) {
        if (Object.prototype.toString.call(sup) === '[object RegExp]') { // 如果是正則表達式,則進行特殊處理
            return new RegExp(sup);
        } else {
            let sub = Object.prototype.toString.call(sup) === '[object Array]' ? [] : {};
            for (let item in sup) { // for in 會對obj的第一層級進行遍歷, item爲鍵名或者數組下標
                if (sup.hasOwnProperty(item)) { // 只對sup私有屬性進行深拷貝
                    if (typeof sup[item] === 'object') { // 如果item是引用類型,則遞歸調用deepClone
                        sub[item] = deepClone(sup[item]);
                    } else {
                        sub[item] = sup[item]; // 如果爲基本類型,則直接複製
                    }
                }
            }
            return sub;
        }
    }

2. 被拷貝對象爲Object,且有正則子對象

對正則對象暫且考慮能否正確拷貝。

    const a = {
        ax: 1,
        aArr: [2, 3, 4, [5, 6]],
        aObj: {
            objx: "a",
            objArr: [2, 3, 4, [5, 6]],
            objO: { oox: 0 },
        	aRegExp: /[a-z]/g,
        }
    }
    const b = deepClone(a);
    a.ax = 2;
    a.aObj.objArr.pop();
    a.aArr.pop();

基本類型順利拷貝,引用類型的修改拷貝前後互不影響,正則對象順利拷貝,null順利拷貝。
在這裏插入圖片描述

3、快捷深拷貝

1. JSON解析

可以深度拷貝引用類型,表現非常出色啦,只是無法拷貝 RegExp 及 undefined

    const a = {
        ax: 1,
        aArr: [2, 3, 4, [5, 6]],
        aObj: {
            objx: "a",
            objArr: [2, 3, 4, [5, 6]],
            objO: { oox: 0 }
        },
        aRegExp: /[a-z]/g,
    }
    const b = JSON.parse(JSON.stringify(a));
    a.aObj.objArr.pop();
    a.aArr.pop();
    a.aRegExp = new RegExp(/[A-Z]/g);

基本類型順利拷貝,包括null,但不能拷貝undefined,引用類型的修改拷貝前後互不影響,但正則表達式的拷貝有誤
在這裏插入圖片描述

4、 常見淺拷貝

1. Object.assgin

可以拷貝基本類型(包括null和undefined)和正則表達式,但是對象和數組是傳址

    const a = {
        ax: 1,
        aArr: [2, 3, 4, [5, 6]],
        aObj: {
            objx: "a",
            objArr: [2, 3, 4, [5, 6]],
            objO: { oox: 0 }
        },
        aRegExp: /[a-z]/g,
    }
    const b = Object.assign(Object.prototype.toString.call(a) === '[object Array]' ? [] : {}, a);
    a.aObj.objArr.pop();
    a.aArr.pop();
    a.aRegExp = new RegExp(/[A-Z]/g);

基本類型順利拷貝,正則對象順利拷貝,null順利拷貝, 但引用類型的修改拷貝前後影響,屬於傳址拷貝。
在這裏插入圖片描述

2. 數組concat

數組內的基本類型及正則表達式傳值賦值,但引用類型爲傳址賦值

    const a = [0, 1, 2, 3, /[a-z]/g, [4, 5, 6], {
        objx: "a",
        objArr: [2, 3, 4, [5, 6]],
        objO: { oox: 0 }
    }, null, undefined,];
    const b = a.concat([]);
    a[0] = 'a';
    a[4] = new RegExp(/[A-Z]/g);
    a[5].pop();
    a[6].objArr.pop();
    console.log(a);
    console.log(b);

基本類型順利拷貝,正則對象順利拷貝,null順利拷貝, 但引用類型的修改拷貝前後影響,屬於傳址拷貝。
在這裏插入圖片描述

3. 解構賦值

基本類型及正則表達式傳值賦值,但引用類型爲傳址賦值

	const a = {
        ax: 1,
        aArr: [2, 3, 4, [5, 6]],
        aObj: {
            objx: "a",
            objArr: [2, 3, 4, [5, 6]],
            objO: { oox: 0 }
        },
        aRegExp: /[a-z]/g,
        aNull: null,
        aUndefined: undefined
    }
   const {ax, aObj, aArr, aRegExp} = a;    
    a.ax = 2;
    a.aObj.objx = 'b';
    a.aObj.objArr.pop();
    a.aArr.pop();
    a.aRegExp = new RegExp(/[A-Z]/g);

基本類型順利拷貝,正則對象順利拷貝,null順利拷貝, 但引用類型的修改拷貝前後影響,屬於傳址拷貝。
在這裏插入圖片描述

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