1. 程式人生 > 其它 >指標巢狀指標 拷貝_淺拷貝與深拷貝

指標巢狀指標 拷貝_淺拷貝與深拷貝

技術標籤:指標巢狀指標 拷貝淺拷貝和深拷貝的區別深拷貝和淺拷貝的區別

在 JavaScript 中有兩中變數型別資料, 基本型別和引用型別. 對值型別的複製操作會對變數值進行拷貝, 兩者互不相干. 而引用型別只會對儲存變數的指標地址進行拷貝, 導致兩個變數指向同一個地址, 也就是同一份資料, 修改其中一個會直接影響另外一個.

而我們要談的淺拷貝與深拷貝就是專指引用型別.

淺拷貝

淺拷貝是最簡單易理解的, 只對引用型別進行一級拷貝.

  • 自行實現
function shallowClone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }


    return target;
} 
  • Object.assign
let obj = {
  a: 1,
  b: 2,
};
let objCopy = Object.assign({}, obj);
console.log(objCopy);
// Result - { a: 1, b: 2 }

上面的程式碼會將所有的可列舉的自身屬性複製到目標物件.

  • 擴充套件操作符 (...)
var sourceObject = {
    a: 1,
    b: function() {
        return a;
    }
};
var targetObject = { ...sourceObject };
console.log(targetObject.b === sourceObject.b); // true

淺拷貝只會對一級進行拷貝, 對於巢狀的引用型別依然是同一個指標地址.

深拷貝

深拷貝會對任意的巢狀層級進行拷貝, 保證所有的引用型別全部是新物件, 不會和原物件有任何的關係, 彼此的修改不會互相影響.

  1. 奇淫巧技 JSON.parse(JSON.stringify(object))
let obj = { 
  a: 1,
  b: { 
    c: 2,
  },
}

let newObj = JSON.parse(JSON.stringify(obj));

obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } 

上面的方法雖然簡單但是有很多的問題.

第一個: 不能拷貝函式

let obj = {
  name: 'neo',
  sayName: function exec() {
    return this.name;
  },
}
let method = JSON.parse(JSON.stringify(obj));
console.log(method); // JSON.parse(JSON.stringify(obj))
/*
{
  name: "neo"
}
*/

第二個: 迴圈引用

迴圈引用是說物件的物件引用了他們自身. 這個是深拷貝中一個老生常談的問題.

// 迴圈引用
let obj = { 
  a: 'a',
  b: { 
    c: 'c',
    d: 'd',
  },
}


obj.c = obj.b;
obj.e = obj.a;
obj.b.c = obj.c;
obj.b.d = obj.b;
obj.b.e = obj.b.c;
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj); 

上面程式碼執行的結果是:

11b5ea9d025ca356da609e8139a4a2c7.png

所以說, JSON.parse(JSON.stringify(obj)) 沒辦法解決迴圈引用的問題.

第三個: 值為 undefined 的屬性會被忽略

var obj = {
    a: 'lendel',
    b: undefined
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// 結果
/*
  { a: 'lendel' }
*/

第四個: Infinity 值會被置為 null

var obj = {
    a: 'lendel',
    i: Infinity
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// 結果
/*
  { a: 'lendel', i: null }
*/

第五個: 一些物件會被轉為字串

這種方法會將一些型別的物件轉變字串, 例如 Date, Set, Map......

var obj = {
    a: 'lendel',
    d: new Date()
};
var newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
// 結果
/*
{ a: 'lendel', d: '2019-04-01T02:53:37.720Z' }
*/

一個簡單的深拷貝方法

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

這是一段非常簡單的深拷貝函式, 因為簡單,所以問題也多. 引數的校驗並不完備, 利用遞迴時當層次過深的時候容易造成棧溢位, 還有迴圈引用.

上面的程式碼體現了深拷貝的基本思想,但實際使用中建議使用 lodash 等現成的庫

對於棧溢位的問題, 我們可以將遞迴改用迴圈來解決:

function loopClone(x) {
    const root = {};


    // 棧
    const loopList = [
        {
            parent: root,
            key: undefined,
            data: x
        }
    ];


    while (loopList.length) {
        // 深度優先
        const node = loopList.pop();
        const parent = node.parent;
        const key = node.key;
        const data = node.data;


        // 初始化賦值目標,key為undefined則拷貝到父元素,否則拷貝到子元素
        let res = parent;
        if (typeof key !== 'undefined') {
            res = parent[key] = {};
        }


        for (let k in data) {
            if (data.hasOwnProperty(k)) {
                if (typeof data[k] === 'object') {
                    // 下一次迴圈
                    loopList.push({
                        parent: res,
                        key: k,
                        data: data[k]
                    });
                } else {
                    res[k] = data[k];
                }
            }
        }
    }


    return roo
程式碼來源: https:// yanhaijing.com/javascri pt/2018/10/10/clone-deep/

結語:

寫的時候我一直在想一個問題, 我究竟需不需要自己寫一個深拷貝函式或類似的. 對於學習來說這是件無關緊要的事, 但是想要在產品中使用自己寫的其實是挺蠢的事. 並不是說我們實現不出來, 而是投入時間的價效比實在太低. 要完成一個工業級別的函式需要大量的測試, 要覆蓋到每一種情況,這是件得不償失的事. 並且是在網上有那麼多經過上千場景驗證過的現成框架的情況下.所以, 要在合適的情況下選擇合適的工具.

文章參考: https:// flaviocopes.com/how-to- clone-javascript-object/ https:// yanhaijing.com/javascri pt/2018/10/10/clone-deep/