TAT.yana 深拷贝与浅拷贝的实现(一)
In Web开发 on 2017年08月08日 by view: 4,043
12

最近的学习中,仔细研究了下深拷贝和浅拷贝,下面就来简单的总结下。

数据类型

首先我们了解下两种数据类型
1、基本类型:像Number、String、Boolean等这种为基本类型
2、复杂类型:Object和Array

浅拷贝与深拷贝的概念


接着我们分别来了解下浅拷贝和深拷贝,深拷贝和浅拷贝是只针对Object和Array这样的复杂类型的。
浅拷贝

可以看出,对于对象或数组类型,当我们将a赋值给b,然后更改b中的属性,a也会随着变化。
也就是说a和b指向了同一块内存,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝。

深拷贝
刚刚我们了解了什么是浅拷贝,那么相应的,如果给b放到新的内存中,将a的各个属性都复制到新内存里,就是深拷贝。
也就是说,当b中的属性有变化的时候,a内的属性不会发生变化。

浅拷贝

那么除了上面简单的赋值引用,还有哪些方法使用了浅拷贝呢?
Object.assign()
在MDN上介绍Object.assign():”Object.assign() 方法用于将所有可枚举的属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。”
复制一个对象

可以看到,Object.assign()拷贝的只是属性值,假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。所以Object.assign()只能用于浅拷贝或是合并对象。这是Object.assign()值得注意的地方。

深拷贝

那么下面我们就来说说复杂的深拷贝

jQuery.extend()
说到深拷贝,第一想到的就是jQuery.extend()方法,下面我们简单看下jQuery.extend()的使用。
jQuery.extend( [deep ], target, object1 [, objectN ] ),其中deep为Boolean类型,如果是true,则进行深拷贝。
我们还是用上面的数据来看下extend()方法。

通过上面的对比可以看出,当使用extend()进行深拷贝的时候,对象的所有属性都添加到target中了。
我们知道了extend()可以进行深拷贝,那么extend()是如何实现深拷贝的呢?
先来看下jQuery.extend()源码

主要看下关于深拷贝的部分,取第一个参数,如果是boolean类型的,就赋值给deep,下面如果deep为true(也就是进行深拷贝),就递归调用extend(),这样就将对象的所有属性都添加到了target中实现了深拷贝。

JSON.parse()和JSON.stringify()
上面的jQuery源码是否让你眼花缭乱?有没有什么办法无脑实现深拷贝呢?JSON.parse()和JSON.stringify()给了我们一个基本的解决办法。

可以看到改变targetCopy并没有改变原始的target,继承的属性也没有丢失,因此实现了基本的深拷贝。
但是用JSON.parse()和JSON.stringify()会有一个问题。
JSON.parse()和JSON.stringify()能正确处理的对象只有Number、String、Array等能够被json表示的数据结构,因此函数这种不能被json表示的类型将不能被正确处理。

上面的例子可以看出,hello这个属性由于是函数类型,使用JSON.parse()和JSON.stringify()后丢失了。
因此JSON.parse()和JSON.stringify()还是需要谨慎使用。

下篇文章我会继续为大家说明深拷贝的各种实现。
未完待续……

原创文章转载请注明:

转载自AlloyTeam:http://www.alloyteam.com/2017/08/12978/

  1. 烽火 2017 年 11 月 2 日

    你这个深拷贝太复杂了 ,你可以考虑下迭代

  2. aman 2017 年 9 月 1 日

    不错,有时候浅拷贝带来的问题不是那么容易发现。

  3. 幾米 2017 年 8 月 28 日

    var target = {a: 1, b: 1};
    var copy1 = {a: 2, b: 2, c: {ca: 21, cb: 22, cc: 23}};
    var copy2 = {c: {ca: 31, cb: 32, cd: 34}};
    var result = $.extend(target, copy1, copy2); // 不进行深拷贝
    console.log(target); // {a: 1, b: 1, c: {ca: 31, cb: 32, cd:34}}

    这个结果错了吧
    应该是:{a: 2, b: 2, c: {ca: 31, cb: 32, cd:34}}

    target作为合并容器,属性相同的值会被最新的覆盖吧。

  4. Jalever 2017 年 8 月 21 日

    浅拷贝中的代码:
    a:2,b:2,c:{ca:31,cb:32,cc:34}

  5. Mutoo 2017 年 8 月 17 日

    深拷贝还必须保证原有对象的内部联系,以下测试用例:

    var foo = {};
    var origin = [foo, foo];
    assert(origin[0] === origin[1]);

    var copy = deepClone(origin);
    assert(copy[0] === copy[1]);

    JSON.stringify()/JSON.parse() 会创建两个不同的 foo 对象,所以不能通过该测试用例。

  6. Confidence 2017 年 8 月 17 日

    第一个代码,哪儿来的33

  7. Andy 2017 年 8 月 16 日

    简单的深拷贝 Object.assign({},obj)

    • deepred 2017 年 9 月 8 日

      Object.assign是浅拷贝啊

      • leslie 2017 年 9 月 20 日

        Object.assign 为什么是浅拷贝?

        • leslie 2017 年 9 月 20 日

          对象中属性包含对象引用的时候,Object.assign 还是用的相同引用

  8. sharkrice 2017 年 8 月 11 日

    JSON.stringify的加上个回调函数应该可以把函数tostring输出出来吧

发表评论