在本次迭代开发中,由于引用对象的原因,导致一个bug,虽然很快得到了解决,但这个问题还是比较经典的。所以就整理下关于深拷贝的问题:

对象

Object.assign

var a={name:'name'}
var b=Object.assign({},a)
var c={...a}
var d=JSON.parse(JSON.stringify(a))
console.log(a===b)//false
console.log(a===c)//false
console.log(a===d)//false

注意

虽然Object.assign返回的是一个新的对象,但是在数据拷贝的过程中,如果某个key对应的value是引用类型的对象,那么Object.assign只会拷贝数据的引用,所以Object.assign其实是浅拷贝

demo1

let a={item:{name:1}}
let b=Object.assign({},a)
b===a //false
b.item===a.item //true
b.item.name = 2//这里其实是首先拿到item的引用,然后修改其name属性
console.log(a.item)//{name:2}
console.log(b.item === a.item)//true

demo2

let a={item:{name:1}};
let b=Object.assign({},a);
let c=a;
console.log(b===a)//false
console.log(b.item === a.item) //true

b.item = {name:3}//由于a和b是不同的引用,占用不同的内存空间,所以这的赋值操作不影响到a,类似demo3中c的赋值操作
console.log(a.item)//{name:1}

c.item = 123//c和a是相同的引用,所以对其属性的赋值操作会映射到a.item
console.log(a.item)//123

demo3 赋值和属性操作

let a={name:1}
let c=a;
let b=a;
c = {name:'c'}//对c的重新赋值
console.log(a)//{name:1}
b.name = 'b'//操作b内存空间的属性name
console.log(a)//{name:'b'}

…运算符和Object.assign行为类似

let a = {name:{item:[1,2,3]}}
let b={...a}//如果有新增属性,在后面新增即可:let b ={...a,name:'neo'}
b==a // false
b.name === a.name//true

深拷贝

  • JSON.string and JSON.parse
  • clone func
  • jQuery.extend
//1
var newObject = JSON.parse(JSON.stringify(oldObject));

//2
function clone(obj) {
      if (obj === null || typeof(obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

      if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
      else
        var temp = obj.constructor();

      for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
          obj['isActiveClone'] = null;
          temp[key] = clone(obj[key]);
          delete obj['isActiveClone'];
        }
      }

      return temp;
    }

// Shallow copy
var newObject = jQuery.extend({}, oldObject);

// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);

针对这几个方法,需要注意的是,通过序列化和反序列化的方式来实现深浅拷贝,有很大的性能问题,而且他解决不了循环引用的问题。所以在日常的开发中,如果遇到来需要深拷贝的问题,最好的解决方式就是避免深拷贝。

MDN Document

数组

slice concat (浅拷贝)

let a=[1,2,3]
let b=a.slice();
let c=a.concat();//或者使用ES7  let c = [...a],转码后也是concat
console.log(a === b)//false
console.log(a === c)//false

本文转载:CSDN博客