深浅拷贝


1. 浅拷贝

创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

简单来说:浅拷贝只拷贝了对象第一层属性的基本类型值,以及第一层的引用地址。

常见的浅拷贝场景:

  • Object.assign()
let a = {
    name: "book",
    book: {
        price: "45"
    }
}
let b = Object.assign({}, a);
a.name = "change";
a.book.price = "55";
console.log(a);  // {name: "change", book: { price: 55 }}
console.log(b);  // {name: "book", book: { price: 55 }}
  • 展开语法 Spread

    let a = {
        name: "book",
        book: {
            price: "45"
        }
    }
    let b = {...a}
    a.name = "change";
    a.book.price = "55";
    console.log(a);  // {name: "change", book: { price: 55 }}
    console.log(b);  // {name: "book", book: { price: 55 }}
    
  • Array.prototype.slice()

    let a = [0, "1", [2, 3]];
    let b = a.slice(1);
    console.log(b);
    // ["1", [2, 3]]
      
    a[1] = "99";
    a[2][0] = 4;
    console.log(a);
    // [0, "99", [4, 3]]
      
    console.log(b);
    //  ["1", [4, 3]]
    

2. 深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。

常见的深拷贝场景:

  • JSON.parse(JSON.stringify(object))

    虽然这种方法,做深拷贝的时候比较简单,但是会有以下几个问题:

    1、会忽略 undefined

    2、会忽略 symbol

    3、不能序列化函数

    4、不能解决循环引用的对象

    5、不能正确处理new Date()

    6、不能处理正则

    • undefinedsymbol 和函数这三种情况,会直接忽略。(因为这三种值不是有效的JSON值,在JSON.stringify的时候会被忽略)。

      let obj = {
          name: 'book',
          a: undefined,
          b: Symbol('muyiy'),
          c: function() {}
      }
      console.log(obj);
      // {
      // 	name: "muyiy", 
      // 	a: undefined, 
      //  b: Symbol(muyiy), 
      //  c: ƒ ()
      // }
          
      let b = JSON.parse(JSON.stringify(obj));
      console.log(b);
      // {name: "book"}
      
    • 循环引用情况下,会报错。

      let obj = {
          a: 1,
          b: {
              c: 2,
         		d: 3
          }
      }
      obj.a = obj.b;
      obj.b.c = obj.a;
          
      let b = JSON.parse(JSON.stringify(obj));
      // Uncaught TypeError: Converting circular structure to JSON
      
    • new Date 情况下,转换结果不正确。

      new Date();
      // Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)
          
      JSON.stringify(new Date());
      // ""2018-12-24T02:59:25.776Z""
          
      JSON.parse(JSON.stringify(new Date()));
      // "2018-12-24T02:59:41.523Z"
      
    • 正则情况下,

      let obj = {
          name: "muyiy",
          a: /'123'/
      }
      console.log(obj);
      // {name: "muyiy", a: /'123'/}
          
      let b = JSON.parse(JSON.stringify(obj));
      console.log(b);
      // {name: "muyiy", a: {}}
      
  • jQuery.extend() 和 lodash.cloneDeep()

3. 总结

和原数据是否指向同一对象 第一层数据为基本数据类型 原数据中包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变会使原数据一同改变 改变会使原数据一同改变