资讯专栏INFORMATION COLUMN

JavaScript对象的深浅复制

B0B0 / 1841人阅读

摘要:本文是我在复制对象方面的一些心得总结,由浅复制到深复制,由只复制简单属性到复制,等复杂属性,层层递进。如有陈述不当之处,烦请指出,不胜感激。下面是一个简单的浅复制实现。

前言

从层次上来看,对象的复制可以简单地分为浅复制和深复制,顾名思义,浅复制是指只复制一层对象的属性,不会复制对象中的对象的属性,对象的深复制会复制对象中层层嵌套的对象的属性。
在复制对象时,除了要复制对象的属性外,还要兼顾到是否保留了对象的constructor属性,是否对每一种数据类型(JavaScript常见的数据类型有String,Number,Boolean,Data,RegExp,Array,Funtion,Object)都实现正确的复制。项目中,我们可以根据实际情况,决定需要实现什么样程度的复制。
本文是我在复制对象方面的一些心得总结,由浅复制到深复制,由只复制简单属性到复制Function,RegExp等复杂属性,层层递进。如有陈述不当之处,烦请指出,不胜感激。

正文 浅复制

浅复制只会依次复制对象的每一个属性,不会对这些属性进行递归复制。下面是一个简单的浅复制实现。

//对象浅复制                                                           
function shadowCopy(obj){
        if(typeof obj !== "object") return obj;

        for(var prop in obj){
            if(obj.hasOwnProperty(prop)){
                newObj[prop] = obj[prop];
            }
        }
        return newObj;
    }

仔细观察,不难发现上述方法的缺陷:
1.不能正确实现数组的浅复制
2.复制操作丢失了对象的constructor属性

好,我们现在已经发现了问题所在,只需针对性地解决,一个还算完美的浅复制对象的方法就诞生了!

//对象浅复制
    function shadowCopy(obj){
            if(typeof obj !== "object") return ;
            var newObj;

            //保留对象的constructor属性
            if(obj.constructor === Array){
                newObj = [];
            } else {
                newObj = {};
                newObj.constructor = obj.constructor;
            }

            for(var prop in obj){
                if(obj.hasOwnProperty(prop)){
                    newObj[prop] = obj[prop];
                }
            }
            return newObj;
        }

浏览器中测试一下:

    var arr1 = [0,1,2];
    console.log(arr1);
    console.log(shadowCopy(arr1));
    
    var arr2 = [0,1,2,[3,4,5]],
        arr2Copy = shadowCopy(arr2);
    console.log(arr2);
    console.log(arr2Copy);
    arr2Copy[3][0] = 6;
    console.log(arr2[3][0]);  //6

Good! 可以正确实现数组复制和并且保留constructor了,但细心的你一定发现了,浅复制后的对象的 arr2Copy[3]arr2[3] 指向的是一个对象,改变其中一个,同时也会改变另一个。我们想要实现的是 复制,但这并不是复制呀!
这是浅复制的一个弊端所在,接下让我们看看深复制是怎样解决这个问题的。

深复制

深复制需要层层递归,复制对象的所有属性,包括对象属性的属性的属性....(晕~)
如果只是需要简单地复制对象的属性,而不用考虑它的constructor,也不用考虑函数,正则,Data等特殊数据类型,那这里有一个深复制的小trick,两行代码即可:

function deepCopy(obj){
    if(typeof obj !== "object"){ return ;}
    var str = JSON.stringify(obj);
    return JSON.parse(str);
}

大多数情况下,上面的就可以满足要求了,但一些时候,我们需要把函数,正则等特殊数据类型也考虑在内,或者当前环境不支持JSON时,上面的方法也就不适用了。这时,我们可以通过递归来实现对象的深层复制,如下:

function deepCopy(obj){
    if(typeof obj !== "object"){ return ;}
    var newObj;

    //保留对象的constructor属性
    if(obj.constructor === Array){
        newObj = [];
    } else {
        newObj = {};
        newObj.constructor = obj.constructor;
    }

    for(var prop in obj){
        if(typeof obj[prop] === "object"){
            if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
                newObj[prop] = obj[prop];
            } else {
                //递归
                newObj[prop] = deepCopy(obj[prop]);
            }
        } else {
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}

先用上面的例子测试:

棒!可以正确实现多维数组的复制,再看是否能实现函数和正则的复制:

function Person(name){
    this.name = name;
    this.age = age;
    this.search = new RegExp(name);
    this.say = function(){
        console.log(this.name + "今年" + this.age + "岁了");
    }
}
var p1 = new Person("Claiyre",20),
    p2 = deepCopy(p1);

console.log(p1);
console.log(p2);

p2.age = 22;
p1.say();
p2.say();

圆满完成!!

稍加整理,我们就可以得到一个较为通用的js对象复制函数:

function deepCopy(obj){
    var newObj = obj.constructor === Array ? []:{};
    newObj.constructor = obj.constructor;

    if(typeof obj !== "object"){ 
        return ;
    } else if(window.JSON){
        //若需要考虑特殊的数据类型,如正则,函数等,需把这个else if去掉即可
        newObj = JSON.parse(JSON.stringify(obj));
    } else {
        for(var prop in obj){
            if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
                newObj[prop] = obj[prop];
            } else if(typeof obj[prop] === "object"){
                //递归
                newObj[prop] = deepCopy(obj[prop]);
            } else {
                newObj[prop] = obj[prop];
            }
        }
    } 
    return newObj;
}
结语

面向对象的编程语言,其核心是对象,因此深入了解对象的相关操作,纵向比较异同,对学习过程是极有好处的。

博客原文地址:Claiyre的个人博客 https://claiyre.github.io/
博客园地址:http://www.cnblogs.com/nuannuan7362/
如需转载,请在文章开头注明原文地址

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/82294.html

相关文章

  • Javascript对象深浅拷贝

    摘要:开门见山,有人叫对象的复制为深复制浅复制,也有人叫深拷贝浅拷贝。高级属性修改深拷贝满足对象的复制,浅拷贝影响原数组。关于对象的深浅拷贝,暂且探索到这里,后续有新发现再进行补充。 showImg(https://segmentfault.com/img/remote/1460000014305581); 开门见山,有人叫对象的复制为深复制浅复制,也有人叫深拷贝浅拷贝。其实都是copy。 ...

    qieangel2013 评论0 收藏0
  • 深浅拷贝

    摘要:深复制实现代码如下第一种方法通过递归解析解决第二种方法通过解析解决作者六师兄链接原生深拷贝的实现处理未输入新对象的情况通过方法构造新的对象 深浅拷贝针对的是 对象类型,如果是字符串的数组用[...arr],还是不会影响 要区分针对数组的深浅拷贝(默认情况为里面没有对象的数组),与针对对象的深浅拷贝 JavaScript数组深拷贝和浅拷贝的两种方法 let a1 = [1, 2]; ...

    Karrdy 评论0 收藏0
  • 复习Javascript专题(四):js中深浅拷贝

    摘要:基本数据类型的复制很简单,就是赋值操作,所以深浅拷贝也是针对,这类引用类型数据。它会抛弃对象的。另外,查资料过程中还看到这么一个词结构化克隆算法还有这一篇资料也有参考,也写得比较详细了的深浅拷贝 基本数据类型的复制很简单,就是赋值操作,所以深浅拷贝也是针对Object,Array这类引用类型数据。 浅拷贝对于字符串来说,是值的复制,而对于对象来说则是对对象地址的复制;而深拷贝的话,它不...

    MobService 评论0 收藏0
  • JavaScript深浅拷贝

    摘要:基本类型指的是简单的数据段,而引用类型指的是一个对象保存在堆内存中的地址,不允许我们直接操作内存中的地址,也就是说不能操作对象的内存空间,所以,我们对对象的操作都只是在操作它的引用而已。 工作中经常会遇到需要复制 JavaScript 数据的时候,遇到 bug 时实在令人头疼;面试中也经常会被问到如何实现一个数据的深浅拷贝,但是你对其中的原理清晰吗?一起来看一下吧! 一、为什么会有深浅...

    Tonny 评论0 收藏0
  • js深浅复制

    摘要:总结综上所述,数组的深拷贝比较简单,方法没有什么争议,对象的深拷贝,比较好的方法是用的方法实现,或者递归实现,比较简单的深复制可以使用实现参考资料知乎中的深拷贝和浅拷贝深入剖析的深复制 深浅复制对比 因为JavaScript存储对象都是存地址的,所以浅复制会导致 obj 和obj1 指向同一块内存地址。我的理解是,这有点类似数据双向绑定,改变了其中一方的内容,都是在原来的内存基础上做...

    Apollo 评论0 收藏0

发表评论

0条评论

B0B0

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<