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)

打印如下:

我们发现,上述的方法会忽略值为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

打印如下:

我们发现,克隆的对象的构造函数已经变成了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/qinyan20/p/10006747.html

时间: 2024-11-05 18:50:17

js对象拷贝的相关文章

js对象浅拷贝和深拷贝详解

js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文为大家分享了JavaScript对象的浅拷贝和深拷贝代码,供大家参考,具体内容如下 1.浅拷贝 拷贝就是把父对像的属性,全部拷贝给子对象. 下面这个函数,就是在做拷贝: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 var

js对象的继承

<!DOCTYPE HTML> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>无标题文档</title> <script> //继承 : 子类不影响父类,子类可以继承父类的一些功能 ( 代码复用 ) //属性的继承 : 调用父类的构造函数 call //

JS对象复制

在JavaScript很多人复制一个对象的时候都是直接用"=",因为大家都觉得脚本语言是没有指针.引用.地址之类的,所以直接用"="就可以把一个对象复制给另外一个对象,如下代码: var i1 = 1; var i2 = i1; i2 = 2; alert("i1:"+i1+",i2:"+i2); 输出结果:i1:1 , i2:2 但可能没有发现,这种“复制”用在对象(object)类型是“错误”的,因为这只是把对象的地址复制

js 深度拷贝

js 数据类型 分为2种: 基本数据类型:Undefined.Null.Boolean.Number.String 复杂数据类型:Object.Array.function 他们的区别是在内存中的存储方式: 简单数据类型:在栈内直接存储值 复杂数据类型:在栈内存储引用 1. 在简单类型赋值的时候是赋的值, 如下 a 的值是不会被修改的 var a = 1; var b = a; b = 2; console.log(a);//1 2. 复杂类型赋值的时候是赋的地址值,如下a对象下的a属性值会变成

JQuery之JQuery的版本 JQuery入门 属性获取 JQuery就绪函数 JS文档就绪函数和JQuery文档就绪函数的区别 JS对象和JQuery对象的区别 关于$的使用 多个JS库的冲突解决方案

JQuery的版本 JQuery入门 属性获取 JQuery就绪函数 JS文档就绪函数和JQuery文档就绪函数的区别 JS对象和JQuery对象的区别 关于$的使用 多个JS库的冲突解决方案 JQuery的版本 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>jQuery的版本</title> <

DOM对象和js对象以及jQuery对象的区别

一.DOM对象 文档对象模型简称DOM,是W3C组织推荐的处理可扩展置标语言的标准编程接口. DOM实际上是以面向对象方式描述的文档模型.DOM定义了表示和修改文档所需的对象.这些对象的行为和属性以及这些对象之间的关系. 通过DOM,可以访问所有的 HTML 元素,连同它们所包含的文本和属性.可以对其中的内容进行修改和删除,同时也可以创建新的元素. HTML DOM 独立于平台和编程语言.它可被任何编程语言诸如 Java.JavaScript 和 VBScript 使用. DOM对象,即是我们用

多个jsp页面共享一个js对象的超级方法

一个jsp页面为A.jsp,在A.jsp中点击一个按钮打开另一个B.jsp页面.思路如下: 在A.jsp打开B.jsp的事件中,写入如下代码: 1 2 window.top['_CACHE'] = chatFrdList; window.top['_CACHE'][frdUserId] = frdUserId; 其中,chatFrdList定义为var chatFrdList = new Object(); frdUserId为一个用户的id. 那么,在B.jsp的一个事件中,就可以执行下面的操

Qt jsoncpp 对象拷贝、删除、函数调用 demo

/***************************************************************************************************** * Qt jsoncpp 对象拷贝.删除.函数调用 demo * 声明: * 本程序主要就是为了验证jsoncpp中Json::Value对象中的append.等号赋值是拷贝内容,还是赋值指针, * 就测试而言,从目前的情况来看应该是拷贝内容,在下面checkAndRemoveAppName(

js对象字面量属性间相互访问的问题 和 这个问题与执行环境的关系

对象字面量属性间无法相互访问的问题: 我曾经在定义一个对象时, 并出于复用性的考虑试图复用某些属性: // test 1 var person = { firstName : "Bill", lastName : "Gates", fullName : firstName + " " + lastName // 这里报错 } alert(person.firstName); alert(person.lastName); alert(person