JS基础——深拷贝、浅拷贝

101 阅读2分钟

深拷贝、浅拷贝

深、浅拷贝都是针对引用类型而言的,浅拷贝只是复制对象的引用,拷贝后的对象和原对象只要有一个发生变化,另一个也会相应变化,而深拷贝则是真正的对对象的拷贝。

【浅拷贝】

只是复制对象的引用,并未实现真正的复制。 常见实现浅拷贝的方法有一下几种:

  1. 简单使用=赋值
  2. 使用Object.assign()
  3. 使用展开运算符(...)
var arr = [1,2,3];
var obj = {a:'a', b:[1,2], c:{cc:'cc'}};
 
var cloneArr = arr;
var cloneObj = obj;
 
cloneArr.push(4);
cloneObj.a = 'aaa';
 
console.log(arr);      // [1, 2, 3, 4]
console.log(cloneArr); // [1, 2, 3, 4]
 
console.log(obj);      // {a:'aaa', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2], c:{cc:'cc'}}

【深拷贝】

实现了真正的复制,拷贝的对象与原对象完全隔离,互不影响。 实现深拷贝的方法主要有两种:

  1. 利用JSON中的parse和stringify
  2. 利用递归来实现每一层重新创建对象并赋值

(一) JSON中是parse和stringify

var arr = [1,2,3];
var obj = {a:'a', b:[1,2], c:{cc:'cc'}};
 
var cloneArr = JSON.parse(JSON.stringify(arr));
var cloneObj = JSON.parse(JSON.stringify(obj));
 
cloneArr.push(4);
cloneObj.a = 'aaa';
cloneObj.b.push(3);
cloneObj.c = 'ccc';
 
console.log(arr);      // [1, 2, 3]
console.log(cloneArr); // [1, 2, 3, 4]
 
console.log(obj);      // {a:'a', b:[1,2], c:{cc:'cc'}}
console.log(cloneObj); // {a:'aaa', b:[1,2,3], c:{cc:'ccc'}}

弊端:对于一些复杂的引用类型,就会出现问题

var obj = {
  name: 'Tom',
  sayName: function(){
    console.log(this.name)
  }
}
 
var cloneObj = JSON.parse(JSON.stringify(obj));
 
console.log(obj);      // {name: "Tom", sayName: ƒ}
console.log(cloneObj); // {name: "Tom"}

(二) 递归实现

利用递归实现深拷贝的思想是每一层都重新创建对象并赋值

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;
}
 
// 测试
var obj = {
  name: 'Tom',
  sayName: function(){
    console.log(this.name)
  }
}
 
var cloneObj = deepClone(obj);
 
console.log(obj);      // {name: "Tom", sayName: ƒ}
console.log(cloneObj); // {name: "Tom", sayName: ƒ}

JavaScript中的有的方法也能实现拷贝,比如:concat()和slice(),ES6中的Object.assgin()和...展开运算符。这里就不一一测试了,直接给出结论吧。

concat 只是对数组的第一层进行深拷贝 slice 只是对数组的第一层进行深拷贝 Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值 ... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值