js拷贝对象之——深拷贝、浅拷贝

在js中,当我们对一个对象进行复制后会发现,改变复制的对象内容时,被复制的对象也进行了相同的改变。
这里就涉及到了一个深拷贝与浅拷贝的问题。深拷贝和浅拷贝是只针对像object、array这样复杂的对象。

js中的对象分为基本类型和复合(引用)类型,前者存放在栈内存,后者存放在堆内存。
堆内存用于存放由new创建的对象,栈内存存放一些基本类型的变量和对象的引用变量

浅拷贝

浅拷贝指的是:对对象地址的复制,不会进行递归复制,并没有开辟新的栈,也就是复制的结果是两个对象指向同一个地址。
eg:

var arr1 = [1,2,3,4,5];
var arr2 = arr1;
arr1.push(6);
console.log(arr1);  //123456
console.log(arr2);  //123456

在例子中可以发现,当我们将arr1复制给arr2后,改变arr2值后,arr1也进行了相应的改变。
浅拷贝的对象只是拷贝了基本类型的数据,而引用类型数据,复制后也是发生引用,指向的地址还是原对象的地址。

深拷贝

深拷贝指的是:将原对象的各个属性逐个复制出去,而且将原对象各个属性所包含的对象也依次采用深复制的方法递归复制到新对象上。并开辟了一块新的内存地址来存放复制的对象。

当对对象a进行深拷贝给对象b后,之后无论是对a操作还是对b操作,都是指改变自己的内容。实现深拷贝的方法有很多种,如下:

1、JSON.stringify/parse的方法

let arr1 = [1,2,3,4];
let arr2 = JSON.parse(JSON.stringify(arr1));
arr2.push(5);
console.log(arr1); //[1, 2, 3, 4]
console.log(arr2); //[1, 2, 3, 4, 5]

const obj1 = {a:'a',b:'b'};
const obj2 = JSON.parse(JSON.stringify(obj1));

0bj2.a = 'aa';

console.log(0bj1); // {a:'a',b:'b'};
console.log(0bj2); // {a:'aa',b:'b'};

对于简单的对象使用该方法是可以正常进行深拷贝的,但是如果对象中含有function则会出现问题

const obj = {
  name:'zhangsan',
  fn:function(){
    console.log('Hello World');
  }
}
console.log(obj); // {name: "zhangsan", fn: ƒ}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "zhangsan"}

JSON.stringify/parse实现深拷贝的时候,需要求目标对象(非 undefined,function)

2、使用递归方法
使用递归方法,就是对每一层的数据都实现一次 创建对象->对象赋值的操作

function deepClone(item){
  const target = item.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
  for(let keys in item){ // 遍历目标
    if(item.hasOwnProperty(keys)){
      if(item[keys] && typeof item[keys] === 'object'){ // 如果值是对象,就递归一下
        target[keys] = item[keys].constructor === Array ? [] : {};
        target[keys] = deepClone(item[keys]);
      }else{ // 如果不是,就直接赋值
        target[keys] = item[keys];
      }
    }
  }
  return target;
}

const obj1 = {a:'a',b:'b'};
const obj2 = deepClone(obj1);

obj2.a = 'aa';

console.log(obj1); // {a:'a',b:'b'};
console.log(obj2); // {a:'aa',b:'b'};

const obj3 = {
  name:'zhangsan',
  fn:function(){
    console.log('Hello World');
  }
}
console.log(obj3); // {name: "zhangsan", fn: ƒ}
const obj4 = deepClone(obj3);
console.log(obj4); // {name: "zhangsan", fn: ƒ}

改函数可以深拷贝数组、对象、以及带函数的对象。

注:如果是数组还可以使用很多方法进行深拷贝:

3、使用es6的Array.from(针对数组)

var arr1=[1,2,3];
var arr2=Array.from(arr1);
arr1.push(4);
console.log(arr1);  //[1,2,3,4]
console.log(arr2);  //[1,2,3,]
arr2.push(5);
console.log(arr1);  //[1,2,3,4]
console.log(arr2);  //[1,2,3,5]

4、使用es6的…(只能进行第一层的深拷贝)

var arr1=[1,2,3];
var arr2=[...arr1];
arr1.push(4);
console.log(arr1);  //[1,2,3,4]
console.log(arr2);  //[1,2,3,]
arr2.push(5);
console.log(arr1);  //[1,2,3,4]
console.log(arr2);  //[1,2,3,5]

还有concat、selice也可以对数组进行深拷贝(只能进行第一层的深拷贝)

总结

  1. 在对象中赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值
  2. 递归是做复杂深拷贝比较合理的方法
  3. JSON深拷贝只能是对象中没有function时可以使用
  4. 数组的深拷贝方法较多,但是大多是只能进行第一层的深拷贝

你可能感兴趣的:(学习笔记)