js物件和陣列的深拷貝筆記
平時總遇到這種問題:將物件obj賦值給變數a,然後改變了變數a中某個值,結果物件obj中對應的值也被改變了,醬紫就有些煩。陣列arr也是此類問題。
然後百度了一下方法,看到一篇不錯的博文,記下來當做筆記。
原文連結:https://www.cnblogs.com/dabingqi/p/8502932.html
一、深拷貝兩種方法:
1、方法一:JSON.stringify()和JSON.parse。(適用於簡單的資料:undefined
、function
、symbol
會在轉換過程中忽略)
let obj = {name:'old',id:1}; let newObj = obj;
newObj.name= 'new'; console.log('old:',obj); //old: {name: "new", id: 1} console.log('new',newObj); //new {name: "new", id: 1}
let obj2 = {name:'old',id:1};
let newObj2 = JSON.parse(JSON.stringify(obj2));
newObj2.name = 'new';
console.log('old:',obj2); //old: {name: "old", id: 1}
console.log('new',newObj2); //new {name: "new", id: 1}
let arr1 = [0,1,2,3,4]; let newArr1 = arr1; newArr1.push(8);
console.log(arr1); //[0, 1, 2, 3, 4, 8] console.log(newArr1); //[0, 1, 2, 3, 4, 8]
let arr2 = [0,1,2,3,4]; let newArr2 = JSON.parse(JSON.stringify(arr2)); newArr2.push(8);
console.log(arr2); //[0, 1, 2, 3, 4] console.log(newArr2); //[0, 1, 2, 3, 4, 8]
2、方法二:遞迴方法。(可以拷貝帶有函式的物件)【以下程式碼複製的參考博文】
function deepClone(source){ const targetObj = source.constructor === Array ? [] : {}; // 判斷複製的目標是陣列還是物件 for(let keys in source){ // 遍歷目標 if(source.hasOwnProperty(keys)){ if(source[keys] && typeof source[keys] === 'object'){ // 如果值是物件,就遞迴一下 targetObj[keys] = source[keys].constructor === Array ? [] : {}; targetObj[keys] = deepClone(source[keys]); }else{ // 如果不是,就直接賦值 targetObj[keys] = source[keys]; } } } return targetObj; }
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}}; const cloneObj = deepClone(originObj); console.log(cloneObj === originObj); // false cloneObj.a = 'aa'; cloneObj.c = [1,1,1]; cloneObj.d.dd = 'doubled'; console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}}; console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
帶函式的: const originObj = { name:'axuebin', sayHello:function(){ console.log('Hello World'); } } console.log(originObj); // {name: "axuebin", sayHello: ƒ} const cloneObj = deepClone(originObj); console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}
二、JavaScript
中陣列自帶的拷貝方法 (首層淺拷貝)【以下程式碼複製的參考博文】
1、concat
資料只有一層,可以實現深拷貝:
const originArray = [1,2,3,4,5]; const cloneArray = originArray.concat(); console.log(cloneArray === originArray); // false cloneArray.push(6); // [1,2,3,4,5,6] console.log(originArray); [1,2,3,4,5];
但是,如果資料不止一層,第二層就又變成淺拷貝了:
const originArray = [1,[1,2,3],{a:1}]; const cloneArray = originArray.concat(); console.log(cloneArray === originArray); // false cloneArray[1].push(4); cloneArray[2].a = 2; console.log(originArray); // [1,[1,2,3,4],{a:2}]
2、slice
多層資料拷貝結果:
const originArray = [1,[1,2,3],{a:1}]; const cloneArray = originArray.slice(); console.log(cloneArray === originArray); // false cloneArray[1].push(4); cloneArray[2].a = 2; console.log(originArray); // [1,[1,2,3,4],{a:2}]
結果:concat和slice都一樣,都是首層淺拷貝,只能深拷貝資料結構簡單,只有一層的資料。
三、ES6 中的拷貝方法 (首層淺拷貝)【以下程式碼複製的參考博文】
1、Object.assign()
Object.assign()
拷貝的是屬性值。假如源物件的屬性值是一個指向物件的引用,它也只拷貝那個引用值。
2、...展開運算子
const originArray = [1,2,3,4,5,[6,7,8]]; const originObj = {a:1,b:{bb:1}}; const cloneArray = [...originArray]; cloneArray[0] = 0; cloneArray[5].push(9); console.log(originArray); // [1,2,3,4,5,[6,7,8,9]] const cloneObj = {...originObj}; cloneObj.a = 2; cloneObj.b.bb = 2; console.log(originObj); // {a:1,b:{bb:2}}
結果:Object.assign()
和...展開運算子都一樣,也是首層淺拷貝。
四、首層淺拷貝【以下程式碼複製的參考博文】
就是對目標物件的第一層進行深拷貝,然後後面的是淺拷貝,可以稱作“首層淺拷貝”。(理論和概念性的東西不太擅長,也直接引用博文中的吧)
例子:
function shallowClone(source) { const targetObj = source.constructor === Array ? [] : {}; // 判斷複製的目標是陣列還是物件 for (let keys in source) { // 遍歷目標 if (source.hasOwnProperty(keys)) { targetObj[keys] = source[keys]; //直接=號賦值 } } return targetObj; }
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}}; const cloneObj = shallowClone(originObj); console.log(cloneObj === originObj); // false cloneObj.a='aa'; cloneObj.c=[1,1,1]; cloneObj.d.dd='surprise';
console.log(cloneObj);
// {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
console.log(originObj);
// {a:'a',b:'b',c:[1,2,3],d:{dd:'surprise'}}
因為在方法中,第一層資料進行了深拷貝,第二層卻用了=號直接賦值,所以原先的第一層資料不受影響,但是第二層開始的資料還是會受影響而發生改變。
總結:1、只有遞迴能完全進行深拷貝;
2、簡單的資料結構可以用JSON.parse和JSON.stringify來進行深拷貝;
3、concat、slice、Object.assign、...展開運算子都只是首層淺拷貝;