JS深拷貝、淺拷貝 和 使用場景 詳解

 基本概念:


ECMAScript 數據類型劃分:

            * 簡單類型(基礎類型, 原始類型):Number、String、Booleanl、Undefined、Symbol(SE6新增的);

            * 引用類型(複雜類型, 複合類型):Object(Math、Nul、Function{ Array、Set、Map、Date、RegExp } );

 

 typeof 運算符的返回值:

            number、string、boolean、undefined、symbol、object、function;

 

 

在瞭解深拷貝之前,應該先了解以下2個概念:

 

傳值:

表示傳遞的是變量所賦的值(就是當一個變量的值 賦給 另一個新變量的時候,新變量修改了值,不會影響原來變量的值)

【簡單來說:就是相當於把變量的值 全新的複製了一個份】。

 

傳址:

表示傳遞的是變量所在的內存地址【內存地址:也就是所謂的指針】(就是當一個變量的值 賦給 另一個新變量的時候,新變量修改了值,那麼原來變量的值也會被修改,因爲它們是同一個內存地址,所以一改全改)

【簡單來說:就是類似給變量起了一個新的名字,但實際上還是原來的】。

 

 

注:一般情況下:簡單類型賦值是 傳值 !!, 複雜類型賦值是 傳址!!。


 

淺拷貝:

當對某個數據(數組或對象)進行拷貝後,修改新數據(拷貝後的數據)裏面第1層的數據是不會影響老數據(被拷貝的數據)的,但是如果還要第2層 或 更深層次的數據(複雜數據類型),它仍然是有關聯的,如果修改了新數據,那麼老數據也會被修改。

淺拷貝一般意義不大,瞭解就行,這裏就不貼代碼了。

根據上面淺拷貝的問題,此時:深拷貝就排上用場了。

 

 

深拷貝:

        * 就是在拷貝數據(數組或對象)時,不管數據裏面有多少層,是簡單 還是 複雜數據類型,只要進行深拷貝後,和老數據(之前被拷貝的數據)就毫無關聯,相互獨立,互不影響!在修改新數據,對老數據毫無影響。

        * 作用:打斷兩個對象或數組之間的引用關係!

        * 原理:在對數組或對象進行拷貝,使用遞歸循環複製,而每次遞歸都是開闢一個新的內存地址(指針),所以一般只要不是同一個內存地址,就不會修改其值。

        * 簡單來講:當拷貝一個數據後,和原來的數據就沒有任何聯繫了,因爲它們不是同一個指針。

 

 深拷貝方式一:JSON.parse(JSON.stringify( )) 序列化

//數組的深拷貝測試
let myArr = ["HTML", ["CSS", "H5", ["html", "head", ["meta", "title", "style"], "body"], "CSS3"], "ES6", "jQuery"]
let newMyArr = JSON.parse(JSON.stringify(myArr));

newMyArr[1][2][2][1] = 'TITLE';
console.log('修改前:', myArr);
console.log('修改後:', newMyArr);




//數組對象深拷貝測試
let arr = ['H5', 'CSS', { a: 'PHP', b: 'Java', c: ['A', 'B', ['一', 2, '三'], 3, { d: '3D' }] }, 'ES6'];
let nArr = JSON.parse(JSON.stringify(arr));
			
nArr[2].a = 'Python';		
nArr[2].c[2][0] = '壹壹壹';
console.log('修改前:', arr);
console.log('修改後:', nArr);





//對象數組深拷貝測試 deepCopy
let obj = { h5: 'H5', cs: 'CSS', back: { a: 'PHP', b: 'Java', c: ['A', 'B', ['一', 2, '三'], 3, { d: '3D' }] }, js: 'ES6', null: null, udf: undefined, fn: function (e) { console.log(666) } };
let nObj = JSON.parse(JSON.stringify(obj));
			
nObj.back.c[0] = 'AAA';
nObj.back.c[2][0] = '壹';
nObj.back.c[2][2] = '叄';
nObj.fn = function(abc){
    alert(888)
}
console.log('修改前:', obj);
console.log('修改後:', nObj);

 

 

深拷貝方式二:deepCopy() 自定義方法



//判斷是否是數組:
const isArray = (arr) => {
    return Object.prototype.toString.call(arr) === '[object Array]';
};




//判斷是否是對象:
const isObject = (obj) => {
    return typeof obj === 'object' && obj !== null;
};




//數組或對象的深拷貝方法1
const deepCopy = (val) => {
    let oVal = isArray(val) ? [] : {};
    for (let v in val) {
        if (isObject(val[v])) {
            //這裏是深拷貝的關鍵所在(遞歸調用)
            oVal[v] = deepCopy(val[v]);
        } else {
            oVal[v] = val[v];
        };
    };
    return oVal;
};




			
//數組或對象的深拷貝方法2
const deepCopy2 = (val) => {
    let oVal = Array.isArray(val) ? [] : {};
    for (let v in val) {
        if (val.hasOwnProperty(v)) {
            if ('object' === typeof val[v]) {
                //這裏是深拷貝的關鍵所在(遞歸調用)
                oVal[v] = deepCopy2(val[v]);
            } else {
                oVal[v] = val[v];
            };
        }
    };
    return oVal;
};




//數組的深拷貝測試
let myArr = ["HTML", ["CSS", "H5", ["html", "head", ["meta", "title", "style"], "body"], "CSS3"], "ES6", "jQuery"]
let newMyArr = deepCopy(myArr);

newMyArr[1][2][2][1] = 'TITLE';
console.log('修改前:', myArr);
console.log('修改後:', newMyArr);




//數組對象深拷貝測試
let arr = ['H5', 'CSS', { a: 'PHP', b: 'Java', c: ['A', 'B', ['一', 2, '三'], 3, { d: '3D' }] }, 'ES6'];
let nArr = deepCopy(arr);
			
nArr[2].a = 'Python';		
nArr[2].c[2][0] = '壹壹壹';
console.log('修改前:', arr);
console.log('修改後:', nArr);





//對象數組深拷貝測試 deepCopy
let obj = { h5: 'H5', cs: 'CSS', back: { a: 'PHP', b: 'Java', c: ['A', 'B', ['一', 2, '三'], 3, { d: '3D' }] }, js: 'ES6', null: null, udf: undefined, fn: function (e) { console.log(666) } };
let nObj = deepCopy(obj);
			
nObj.back.c[0] = 'AAA';
nObj.back.c[2][0] = '壹';
nObj.back.c[2][2] = '叄';
nObj.fn = function(abc){
    alert(888)
}
console.log('修改前:', obj);
console.log('修改後:', nObj);



//對象數組深拷貝測試 deepCopy2
let obj2 = { h5: 'H5', cs: 'CSS', back: { a: 'PHP', b: 'Java', c: ['A', 'B', ['一', 2, '三'], 3, { d: '3D' }] }, js: 'ES6', null: null, udf: undefined, fn: function (e) { console.log(666) } };

let nObj2 = deepCopy2(obj);
nObj2.back.c[0] = 'AAA';
nObj2.back.c[2][0] = '壹';
nObj2.back.c[2][2] = '叄';
nObj2.fn = function(abc){
    alert(888)
}
console.log('修改前:', obj2);
console.log('修改後:', nObj2);



 

通過以上測試,無論我們是對數組 還是 對象進行深拷貝後,修改了新的數組 或 對象後,不會對舊的數組或對象進行修改,它們都是相互獨立的!

 

使用場景:

無論是淺拷貝還是深拷貝,一般都用於操作Object 或 Array之類的複合類型。

比如想對某個數組 或 對象的值進行修改,但是又要保留原來數組 或 對象的值不被修改,此時就可以用深拷貝來創建一個新的數組 或 對象,從而達到操作(修改)新的數組 或 對象時,保留原來數組 或 對象。

在JS中還有一些已經封裝好的方法:

如數組方法:concat(),filter(),slice(),map()等。

它們在修改數組時,不會修改原來的數組,而是返回一個新的數組。

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