在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/wangyulue/articles/7684515.html#4096147

JS中如何进行对象的深拷贝的更多相关文章

  1. js 中数组或者对象的深拷贝和浅拷贝

    浅拷贝 : 就是两个js 对象指向同一块内存地址,所以当obj1 ,obj2指向obj3的时候,一旦其中一个改变,其他的便会改变! 深拷贝:就是重新复制一块内存,这样就不会互相影响. 有些时候我们定义 ...

  2. JS中的event 对象详解

    JS中的event 对象详解   JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...

  3. js中两个对象的比较

    代码取自于underscore.js 1.8.3的isEqual函数. 做了一些小小的修改,主要是Function的比较修改. 自己也加了一些代码解读. <!DOCTYPE html> & ...

  4. MVC中处理Json和JS中处理Json对象

    MVC中处理Json和JS中处理Json对象 ASP.NET MVC 很好的封装了Json,本文介绍MVC中处理Json和JS中处理Json对象,并提供详细的示例代码供参考. MVC中已经很好的封装了 ...

  5. js中的json对象详细介绍

    JSON一种简单的数据格式,比xml更轻巧,在JavaScript中处理JSON数据不需要任何特殊的API或工具包,下面为大家详细介绍下js中的json对象, 1.JSON(JavaScript Ob ...

  6. JavaScript -- 时光流逝(五):js中的 Date 对象的方法

    JavaScript -- 知识点回顾篇(五):js中的 Date 对象的方法 Date 对象: 用于处理日期和时间. 1. Date对象的方法 <script type="text/ ...

  7. JavaScript -- 时光流逝(四):js中的 Math 对象的属性和方法

    JavaScript -- 知识点回顾篇(四):js中的 Math 对象的属性和方法 1. Math 对象的属性 (1) E :返回算术常量 e,即自然对数的底数(约等于2.718). (2) LN2 ...

  8. JavaScript -- 时光流逝(三):js中的 String 对象的方法

    JavaScript -- 知识点回顾篇(三):js中的 String 对象的方法 (1) anchor(): 创建 HTML 锚. <script type="text/javasc ...

  9. js中 给json对象添加属性和json数组添加元素

    js中 给json对象添加新的属性 比如现在有一个json对象为jsonObj,需要给这个对象添加新的属性newParam,同时给newParam赋值为pre.做法如下: var obj={ &quo ...

随机推荐

  1. Service Fabric service 根据环境变量读取配置文件

    前言 一个服务或者产品,往往需要三个环境:一个开发环境(Development),一个测试环境(Staging),一个生产环境(Production), 这就不可避免的需要多个配置文件来匹配相应的环境 ...

  2. 产品管理开发之Git工作流和分支规范推荐

    前言 无论是开源项目还是内部项目,使用Git都是大势所趋,尤其是在产品管理这块,使用Git大大提高了开发效率和产品的交付频率.本篇,针对Git的工作流和分支使用,进行了一些推荐. 目录 1     产 ...

  3. 递归一个List<T>,可自己根据需要改造为通用型。

    /// <summary> /// 递归一个List<ProvinceOrg> /// </summary> /// <returns></ret ...

  4. [PHP]引用返回与节省内存

    PHP中的引用是什么:1.在 PHP 中引用意味着用不同的名字访问同一个变量内容2.引用可以被看作是 Unix 文件系统中的硬链接. 3.使用unset的话,只是删除他这个名字自身对内容的引用,并没有 ...

  5. Dynamics 365 CE命令栏按钮点击后刷新表单页面方法

    微软动态CRM专家罗勇 ,回复326或者20190428可方便获取本文,同时可以在第一间得到我发布的最新博文信息,follow me! Dynamics 365 Customer Engagement ...

  6. 第七周LINUX学习笔记

    HTTP的基本操作 3月17资源子网和通信子网套接字:两类    socket:IP port             IP:port            Ip.port unix sock:基于文 ...

  7. rabbitmq消费者“无故消失”

    hi,all   导读:9月1号17:12左右,发现影子队列存在大量“unacked”(收到了消息,但是还没有手动确认消息)的消息,一段时间后“unacked”的数量没有减少,但是观察消费者端的日志, ...

  8. kubernetes 安装备注

    一.安装环境 阿里云:centos 7.3 master节点:外网IP(116.62.205.90).内网IP(172.16.223.200) node节点:外网IP(116.62.212.174). ...

  9. 【STM32H7教程】第2章 STM32H7的开发环境搭建

    完整教程下载地址:http://forum.armfly.com/forum.php?mod=viewthread&tid=86980 第2章    STM32H7的开发环境搭建 本章主要为大 ...

  10. 微信小程序之onLaunch与onload异步问题

    所述问题: 前端时间开发了一个微信小程序商城项目,因为这个项目我们的需求是进入小程序就通过wx.login({}) 这个api进行用户登录,获取系统后台的用户基本信息.再此之前,一直以为微信小程序中的 ...