JS中如何进行对象的深拷贝

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

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

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

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

上述代码中,使用了=进行赋值,因而b指向了a所指向的栈的对象,也就是a与b指向了同一个栈对象,因此在对b.name赋值时,a.name也发生了变化。为了不上面的状况,能够对对象进行拷贝,代码以下:html

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是一个对象的引用,而不是一个字符串,那么上面的代码也会遇到一些问题,参考以下代码:java

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也一样是这个栈对象的引用,不少时候,咱们不想让这种事情发生,因此咱们就须要用到对象的深拷贝。算法

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

一般状况下,咱们可使用JSON.parse()与 JSON.stringify()实现对象的深克隆,以下:json

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类型的支持也不太友好。函数

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

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

打印以下:
Alt textthis

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

3、目前没有发现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; }; 

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


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

原文:https://www.cnblogs.com/wangyulue/articles/7684515.html#4096147var 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; }; 
相关文章
相关标签/搜索