美文网首页
深拷贝和浅拷贝简述

深拷贝和浅拷贝简述

作者: 高冷的咸鱼 | 来源:发表于2017-12-31 01:38 被阅读0次

    最近在学习JavaScript,学习过程中发现了一个例程中使用了Object.assign来进行对象的拷贝。于是自己做了一个小测试,发现了如下的问题。

    example 1:

    var a = 0;
    var b = a;
    
    console.log(a); // => 0
    console.log(b); // => 0
    
    b = 1;
    
    console.log(a); // => 0
    console.log(b); // => 1
    

    如上,第一次打印输出,a和b都为0,b赋值为1后再打印输出,a不变,b为1。

    example 2:

    var a = {x : 0};
    var b = a;
    
    console.log(a); // => { x: 0}
    console.log(b); // => { x: 0}
    
    b.x = 1;
    
    console.log(a); // => { x: 1}
    console.log(b); // => { x: 1}
    

    如上,第一次打印输出,a和b都为{ x: 1},第二次打印输出,a和b都为{ x: 1}

    至此,问题已经体现出来了:

    当a和b都是数字时,b的改变不会影响到a;
    当a和b都是对象时,b的改变造成了a的改变。


    我们在进行这两个程序编程时,都会意识到以下两点:

    1. 我们将a拷贝给了b
    2. 我们改变了b

    但是我们没考虑到的是,在example 2中,a受到了b的影响,这显然不是我们期望得到的结果。


    而以上所提及的,就是深拷贝和浅拷贝的区别。以下,我们先对深拷贝和浅拷贝做一个理解。

    浅拷贝Shallow Copy,是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

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

    也就是说,深拷贝是开辟了新的内存空间来接受目标,而浅拷贝是把目标映射到了原来的内存上了。所以,当使用浅拷贝时,两个参数实际只是一处地址的两个别名,改变任意参数,实际改变的是对应地址内存中的数据,就会造成另一个同样指向这个地址的参数。


    我发现在JavaScript、C++、java等语言中都会有深拷贝和浅拷贝的概念。在我看来究其原因就是为了所谓的安全操作而摒弃了指针这个概念。在有指针概念的语言,比如C语言中,会出现以下的赋值语句:

    
    int a = 5;
    
    int b = a;
    
    int* p = &a;
    
    

    不难看出的是:

    第一条语句中,声明并定义了a,声明a为整型,开辟了内存空间,并将5赋值给a;
    第二条语句中,声明并定义了b,声明b为整型,开辟了内存空间,并将a=5赋值给b;
    第三条语句中,声明并定义了指针p,声明p为一个指针,开辟了内存空间,并将a的地址赋给了p。

    这样,我们操作b时,实际上时和a没有任何关系的,因为本身就是两处内存。而进行诸如
    p = 10的操作时,实际上是在操作和a所对应的同一处内存,这样a的值也就发生了改变。而类似javaScript语言在进行拷贝操作时,为了其性能,会把比较复杂的对象的拷贝默认为浅拷贝,也就是类似上述的第三条语句的操作,只是简单的做了一个地址的映射,这样就导致了文章开头example 2中出现的问题。


    那么在javaScript中,如何进行对象的深拷贝?

    第一种,最简单粗暴的方法,由于当进行数字这种简单的类型的拷贝使用的是深拷贝,那么我们就先给目标开辟一个空间,然后手动的将所有值都提取出来并一一对应:

    var a = {x : 0, y : 0};
    var b = {};
    
    b.x = a.x;
    b.y = a.y;
    
    console.log(a); // => { x: 0, y : 0}
    console.log(b); // => { x: 0, y : 0}
    
    b.x = 1;
    
    console.log(a); // => { x: 0, y : 0}
    console.log(b); // => { x: 1, y : 0}
    

    第二种,在ES6中提供了Object.assign,也就是开头提到的方法,来完成深拷贝:

    var a = {x : 0, y : 0};
    var b = Object.assign({}, a);
    
    console.log(a); // => { x: 0, y : 0}
    console.log(b); // => { x: 0, y : 0}
    
    b.x = 1;
    
    console.log(a); // => { x: 0, y : 0}
    console.log(b); // => { x: 1, y : 0}
    

    深拷贝是一个重要的也是比较难的部分,这里只是为了解释深拷贝和浅拷贝,没有对深拷贝实现做太多工作。有需要和兴趣的可以自己找找资料。

    相关文章

      网友评论

          本文标题:深拷贝和浅拷贝简述

          本文链接:https://www.haomeiwen.com/subject/ubkogxtx.html