前端 js 深淺拷貝

1、深淺拷貝

        我們瞭解到對象(引用)類型在賦值的過程中其實是複製了地址,從而會導致改變了一方其他也都被改變的情況。通常在開發中我們不希望出現這樣的問題。

let a = {
  age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2  希望是1
let a = {
  name: 'xxx',
  obj: {
    aa: 3
    }
}
let b = a
a.obj.aa = 5
console.log(b.obj.aa) // 5  希望是3

1)淺拷貝

如果我們要複製對象的所有屬性都不是引用類型時,就可以使用淺拷貝,實現方式就是遍歷並複製,最後返回新的對象。

function shallowCopy(obj) {
    var copy = {};
    // 只複製可遍歷的屬性
    for (key in obj) {
        // 只複製本身擁有的屬性
        if (obj.hasOwnProperty(key)) {
            copy[key] = obj[key];
        }
    }
    return copy;
}
let a = {
  age: 1
}
let b = shallowCopy(a)
a.age = 2
console.log(b.age) // 1

JS內部實現了淺拷貝,如Object.assign(target, source),其中第一個參數是我們最終複製的目標對象,後面的所有參數是我們的即將複製的源對象,支持對象或數組,一般調用的方式爲

var newObj = Object.assign({}, originObj);

2)深拷貝

其實深拷貝可以拆分成 2 步,淺拷貝 + 遞歸,淺拷貝時判斷屬性值是否是對象,如果是對象就進行遞歸操作,兩個一結合就實現了深拷貝。

 1、簡單實現

function cloneDeep(source) {
    var target = {};
    for(var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (typeof source[key] === 'object') { //問題一  typeof null === 'object' 傳入null 會返回{} 應該返回 null
            // 問題二 數組會轉成對象 [1,2] 會變成 {0: 1, 1: 2}
                target[key] = cloneDeep(source[key]); // 注意這裏
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}
let a = {
  name: 'xxx',
  obj: {
    aa: 3
    }
}
let b = cloneDeep(a)
a.obj.aa = 5
console.log(b.obj.aa) // 3

一個簡單的深拷貝就完成了,但是這個實現還存在很多問題。

  • 1、因爲 typeof null === 'object',傳入 null 時應該返回 null 而不是 {}

  • 2、沒有考慮數組的兼容, 傳入 [1,2 ]時 應該返回 [1,2] 而不是 {0:1, 1:2}

2、:解決以上兩個問題

function cloneDeep2(source) {

    if (!isObject(source)) return source; // 非對象返回自身
    // if(source === null) return null 
    
    //解決數組兼容
    var target = Array.isArray(source) ? [] : {};
    
    for(var key in source) {
        if (Object.prototype.hasOwnProperty.call(source, key)) {
            if (isObject(source[key])) {
                target[key] = cloneDeep2(source[key]); // 注意這裏
            } else {
                target[key] = source[key];
            }
        }
    }
    return target;
}


// 解決typeof null === 'object'
function isObject(obj) {
    return typeof obj === 'object' && obj != null;
}

3、

function deepClone(source) { //遞歸拷貝
    if(source === null) return null; //null 的情況
    if(source instanceof RegExp) return new RegExp(source);
    if(source instanceof Date) return new Date(source);
    if(typeof source !== 'object') {
        //如果不是複雜數據類型,直接返回
        return source;
    }
    //解決數組兼容
    var target = Array.isArray(source) ? [] : {};

    for(let key in source) {
        //如果 source[key] 是複雜數據類型,遞歸
        target[key] = deepClone(source[key]);
    }
    return target;
}

 

 

 

。。。

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