平时总遇到这种问题:将对象obj赋值给变量a,然后改变了变量a中某个值,结果对象obj中对应的值也被改变了,酱紫就有些烦。数组arr也是此类问题。

然后百度了一下方法,看到一篇不错的博文,记下来当做笔记。

原文链接:

 

 一、深拷贝两种方法:

  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、...展开运算符都只是首层浅拷贝;