zoukankan      html  css  js  c++  java
  • JS中如何进行对象的深拷贝

    在JS中,一般的=号传递的都是对象/数组的引用,并没有真正地拷贝一个对象,那如何进行对象的深度拷贝呢?如果你对此也有疑问,这篇文章或许能够帮助到你

    一、对象引用、浅层拷贝与深层拷贝的区别

    js的对象引用传递理解起来很简单,参考如下代码:

    var a = {name:'wanger'}
    var b = a ;
    a===b // true
    b.name = 'zhangsan'
    a.name //'zhangan'

    上述代码中,使用了=进行赋值,于是b指向了a所指向的栈的对象,也就是a与b指向了同一个栈对象,所以在对b.name赋值时,a.name也发生了变化。为了避免上面的情况,可以对对象进行拷贝,代码如下:

    var a = {name:'wanger'}
    var b = Object.assign({}, a)
    a===b // false
    b.name = 'zhangsan'
    a.name //'wanger'

    上面代码将原始对象拷贝到一个空对象,就得到了原始对象的克隆,这时候a与b指向的是不同的栈对象,所以对b.name重新复制也不会影响到a.name。但是如果a.name是一个对象的引用,而不是一个字符串,那么上面的代码也会遇到一些问题,参考如下代码:

    var a = {name:{firstName:'wang',lastName:'er'}}
    var b = Object.assign({}, a)
    a===b // false
    b.name.firstName = 'zhang'
    a.name.firstName //'zhang'

    b.name.firstName又影响到了a.name.firstName,这是因为Object.assign()方法只是浅层拷贝,a.name是一个栈对象的引用,赋值给b时,b.name也同样是这个栈对象的引用,很多时候,我们不想让这种事情发生,所以我们就需要用到对象的深拷贝。

    二、使用JSON.parse()与JSON.stringify()对对象进行拷贝

    通常情况下,我们可以使用JSON.parse()与 JSON.stringify()实现对象的深克隆,如下:

    var clone = function (obj) {
        return JSON.parse(JSON.stringify(obj));
    }

    这种方法只适用于纯数据json对象的深度克隆,因为有些时候,这种方法也有缺陷,参考如下代码:

    var clone = function (obj) {
        return JSON.parse(JSON.stringify(obj));
    }
    var a = {a:function(){console.log('hello world')},b:{c:1},c:[1,2,3],d:"wanger",e:new Date(),f:null,g:undefined}
    var b = clone(a)

    打印如下:
    Alt text
    我们发现,上述的方法会忽略值为function以及undefied的字段,而且对date类型的支持也不太友好。

    更要紧的是,上述方法只能克隆原始对象自身的值,不能克隆它继承的值,参考如下代码:

    function Person (name) {
        this.name = name
    }
    var wanger = new Person('王二')
    var newwanger = clone(wanger)
    wanger.constructor === Person // true
    newwanger.constructor === Object // true

    打印如下:
    Alt text

    我们发现,克隆的对象的构造函数已经变成了Object,而原来的对象的构造是Person。

    三、目前没有发现bug的对象深拷贝方法

    王二在网上参考了不少文章,方法都不尽完美,于是在前人基础上改造了一下,方法如下,目前没有发现有什么bug:

    var clone = function (obj) { 
        if(obj === null) return null 
        if(typeof obj !== 'object') return obj;
        if(obj.constructor===Date) return new Date(obj); 
        var newObj = new obj.constructor ();  //保持继承链
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {   //不遍历其原型链上的属性
                var val = obj[key];
                newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
            }
        }  
        return newObj;  
    }; 

    这里有三点需要注意:
    1、用new obj.constructor ()构造函数新建一个空的对象,而不是使用{}或者[],这样可以保持原形链的继承;
    2、用obj.hasOwnProperty(key)来判断属性是否来自原型链上,因为for..in..也会遍历其原型链上的可枚举属性。
    3、上面的函数用到递归算法,在函数有名字,而且名字以后也不会变的情况下,这样定义没有问题。但问题是这个函数的执行与函数名 factorial 紧紧耦合在了一起。为了消除这种紧密耦合的现象,需要使用 arguments.callee


    2017-10-03添加,之前没有考虑正则对象的问题,这里再做一下修改:

    var clone = function (obj) { 
        if(obj === null) return null 
        if(typeof obj !== 'object') return obj;
        if(obj.constructor===Date) return new Date(obj); 
        if(obj.constructor === RegExp) return new RegExp(obj);
        var newObj = new obj.constructor ();  //保持继承链
        for (var key in obj) {
            if (obj.hasOwnProperty(key)) {   //不遍历其原型链上的属性
                var val = obj[key];
                newObj[key] = typeof val === 'object' ? arguments.callee(val) : val; // 使用arguments.callee解除与函数名的耦合
            }
        }  
        return newObj;  
    }; 
    原文:https://www.cnblogs.com/wangyulue/articles/7684515.html#4096147
  • 相关阅读:
    if __name__
    Python为什么要self
    ubuntu系统中的svn三连
    Python中读取到16进制数如何转成有符号数值
    知网
    Ubuntu 登陆后黑屏 问题解决
    Ubuntu 开机Recovery-Mode,命令行中操作提示 Read-Only File System 只读文件系统的 问题解决
    句子:霓裳虽美始于宫娥之糙手
    VMware 虚拟机 不能打开的解决方案汇总
    Ubuntu 系统安装 数据恢复软件 ext4magic 通过RPM方式
  • 原文地址:https://www.cnblogs.com/kelly-sunshine/p/10767804.html
Copyright © 2011-2022 走看看