基本概念:
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()等。
它們在修改數組時,不會修改原來的數組,而是返回一個新的數組。