JavaScript系列--深淺拷貝簡單實現

一、前言

之前寫了一篇:淺析JavaScript解析賦值、淺拷貝和深拷貝的區別https://www.mwcxs.top/page/592.html。裏面介紹瞭解析賦值,淺拷貝,深拷貝的原理和實現。淺拷貝方法:Object.assign(),展開語法Spread,Array.prototype.alice(),array.prototype.concat()。深拷貝方法:JSON.parse(JSON.stringify(object)),對於undefined,symbol和函數的會直接忽略。

javascript之Object.assign實現淺拷貝的原理以及實現:https://www.mwcxs.top/page/595.html,淺拷貝Object.assign()原理實現。

 

二、數組淺拷貝

如果是數組,可以使用數組的一些方法實現:slice(),concat()返回一個新數組的特性實現拷貝。

舉個例子:

用concat()實現

var arr = ['saucxs', 1, true, null, undefined];

var new_arr = arr.concat();

new_arr[0] = 'new';

console.log(arr) // ["saucxs", 1, true, null, undefined]
console.log(new_arr) // ["new", 1, true, null, undefined]

用slice實現

var new_arr = arr.slice();

但是如果數組嵌套了對象或者數組的話,比如:

var arr = [{old: 'old'}, ['old']];

var new_arr = arr.concat();

arr[0].old = 'new';
arr[1][0] = 'new';

console.log(arr) // [{old: 'new'}, ['new']]
console.log(new_arr) // [{old: 'new'}, ['new']]

發現:無論是新數組還是舊數組都發生了變化,也就是說使用 concat 方法,克隆的並不徹底

 

用擴展運算符spread實現

var arr = [{old: 'old'}, ['old']];

var new_arr =[...arr];

arr[0].old = 'new';
arr[1][0] = 'new';

console.log(arr) // [{old: 'new'}, ['new']]
console.log(new_arr) // [{old: 'new'}, ['new']]

 

總結

如果數組元素是基本類型,就會拷貝一份,互不影響,而如果是對象或者數組,就會只拷貝對象和數組的引用,這樣我們無論在新舊數組進行了修改,兩者都會發生變化。

我們把這種複製引用的拷貝方法稱之爲淺拷貝,與之對應的就是深拷貝,深拷貝就是指完全的拷貝一個對象,即使嵌套了對象,兩者也相互分離,修改一個對象的屬性,也不會影響另一個。

所以我們可以看出使用 concat , slice, 擴展預算符spread 是一種淺拷貝。

 

三、數組深拷貝

JSON.parse(JSON.stringify())不僅適用於數組還適用於對象。

var arr = ['old', 1, true, ['old1', 'old2'], {old: 1}]

var new_arr = JSON.parse( JSON.stringify(arr) );

console.log(new_arr);

這個方法是一個簡單粗暴的好方法,就是有一個問題,不能拷貝函數,undefined,symbol。

我們舉個例子看一下:

var arr = [
    function(){ console.log(a)}, 
    { b: function(){ console.log(b)}},
    undefined,
    Symbol('saucxs')
]

var new_arr = JSON.parse(JSON.stringify(arr));

console.log(new_arr);   //[null, {}, null, null]

 

四、淺拷貝的實現

接下來我們思考下如何實現一個對象或者數組的淺拷貝。

想一想,好像很簡單,遍歷對象,然後把屬性和屬性值都放在一個新的對象裏。

var shallowCopy = function(obj) {
    // 只拷貝對象
    if (typeof obj !== 'object') return;
    // 根據obj的類型判斷是新建一個數組還是對象
    var newObj = obj instanceof Array ? [] : {};
    // 遍歷obj,並且判斷是obj的屬性才拷貝
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }
    return newObj;
}

 

五、深拷貝的實現

如何實現一個深拷貝呢?說起來也好簡單,我們在拷貝的時候判斷一下屬性值的類型,如果是對象,我們遞歸調用深拷貝函數不就好。

var deepCopy = function(obj) {
    if (typeof obj !== 'object') return;
    var newObj = obj instanceof Array ? [] : {};
    for (var key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
        }
    }
    return newObj;
}

 

六、注意事項

儘管使用深拷貝會完全的克隆一個新對象,不會產生副作用,但是深拷貝因爲使用遞歸,性能會不如淺拷貝,在開發中,還是要根據實際情況進行選擇。

 

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