1. 程式人生 > 實用技巧 >js物件和陣列的深拷貝筆記

js物件和陣列的深拷貝筆記

平時總遇到這種問題:將物件obj賦值給變數a,然後改變了變數a中某個值,結果物件obj中對應的值也被改變了,醬紫就有些煩。陣列arr也是此類問題。

然後百度了一下方法,看到一篇不錯的博文,記下來當做筆記。

原文連結:https://www.cnblogs.com/dabingqi/p/8502932.html

一、深拷貝兩種方法:

  1、方法一:JSON.stringify()和JSON.parse。(適用於簡單的資料:undefinedfunctionsymbol會在轉換過程中忽略)

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、...展開運算子都只是首層淺拷貝;

    原文連結:https://www.cnblogs.com/dabingqi/p/8502932.html