• JavaScript中的深拷贝和浅拷贝


    JavaScript中的深拷贝和浅拷贝

    在 JavaScript 中有不同的方法来复制对象,可以通过 赋值、深拷贝、浅拷贝来实现对象的复制。 其中,我们需要了解一下什么是深拷贝和浅拷贝,和赋值有什么区别。深拷贝和浅拷贝实现的方法都有哪些?
     
    浅拷贝和深拷贝
    • 浅拷贝是创建一个新的对象,这个有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址。所以如果对象是引用类型,进行浅拷贝。 新对象只是拷贝了旧对象的内存地址,新旧对象的地址指向的是堆中的同一个对象。一个对象改变了,就会影响到另一个对象。
    • 深拷贝是将一个对象从内存中完整的拷贝一份出来,并从堆中开辟一个新的区域存放新对象,所以深拷贝后的新对象的地址指向了一个新的堆内存。 改变一个对象,不会影响到原来的旧对象。
    1. var a1 = {b: {c: {} }};
    2. var a2 = shallowClone(a1);   // 浅拷贝方法
    3. a2.b.c == a1.b.c;   // true 新旧对象还是共享同一块内存
    4. var a3 = deepClone(a1);   // 深拷贝方法
    5. a3.b.c == a1.b.c   // false 新对象跟原对象不共享内存。
    总结:浅拷贝对于基本类型,拷贝就是基本类型的本身,但如果是引用类型,浅拷贝拷贝的是指向某个对象的指针,而不是复制对象本身, 新旧对象还是共享同一块内存。深拷贝会拷贝一份和原对象一摸一样的对象, 新旧对象不再共享内存,修改对象不会改变原对象。
     
    赋值和深 / 浅拷贝的区别
    这三者的区别,比较的前提都是针对引用类型:
    • 当我们把一个对象赋值给一个新的变量时,赋值的其实是该对象在栈中的地址,而不是堆中的数据。也就是 两个对象指向的是同一个存储空间,所以无论哪个对象发生改变,改变的都是存储空间中的内容,另一个对象也会发生改变。
    • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因为共享同一块内存,所以会相互影响。
    • 深拷贝:从堆中重新创建一个新的区域存放新的对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象相互不影响。

    上面的列子中,obj1是原始的对象,obj2是赋值得到的对象,obj3是浅拷贝得到的对象,obj4是深拷贝得到的对象。我们可以得出下面的结论:

      

     
    浅拷贝的实现方式

    1.Object.assign()

    Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

    2.展开运算符 ...

    展开运算符是一个 es6 / es2015特性,它提供了一种非常方便的方式来执行浅拷贝,这与Object.assign() 功能相同。

    3.Array.prototype.concat()

    4.Array.prototype.slice()

    5.函数库lodash的 _.clone 方法

     
    深拷贝的实现方式

    1.JSON.parse(JSON.stringify())

    这是利用JSON.stringify()将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象。 这样新的对象就产生了,而且新对象会开辟新的栈,实现深拷贝。 这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则。 因为这两者基于JSON.stringify()和JSON.parse()处理后,得到的正则就不再是正则(变为空对象), 得到的函数就不再是函数(变为null)了。

    2.函数库lodash的 _.cloneDeep 方法

    3.jQuery.extend()方法

    jQuery有提供一个 $.extend 可以用来做深拷贝。
    $.extend(deepCopy, target, object1, [objectN]) //第一个参数为true,就是深拷贝

    4.手写递归方法

    递归方法实现深度拷贝的原理:遍历对象、数组直到里面的数据都是基本的数据类型,然后再去复制,就是深度拷贝。
    有种特殊情况需注意就是对象存在循环引用的情况,即对象的属性直接的引用了自身的情况,解决循环引用问题,我们 可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话 直接返回,如果没有的话继续拷贝,这样就巧妙的化解了循环引用的问题。

  • 相关阅读:
    如何给LG gram写一个Linux下的驱动?
    题解 CF492C Vanya and Exams
    题解 CF546B Soldier and Badges
    题解 P1283 【平板涂色】
    题解 P1717 【钓鱼】
    题解 UVA663 Sorting Slides(烦人的幻灯片)
    题解 P2835 【刻录光盘】
    题解 P5367 【【模板】康托展开】
    题解 P2949 【[USACO09OPEN]工作调度Work Scheduling】
    题解 P2272 【[ZJOI2007]最大半连通子图】
  • 原文地址:https://www.cnblogs.com/aloneer/p/13559067.html
Copyright © 2020-2023  润新知