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/wangyulue/articles/7684515.html#4096147
JS中如何进行对象的深拷贝的更多相关文章
- js 中数组或者对象的深拷贝和浅拷贝
浅拷贝 : 就是两个js 对象指向同一块内存地址,所以当obj1 ,obj2指向obj3的时候,一旦其中一个改变,其他的便会改变! 深拷贝:就是重新复制一块内存,这样就不会互相影响. 有些时候我们定义 ...
- JS中的event 对象详解
JS中的event 对象详解 JS的event对象 Event属性和方法:1. type:事件的类型,如onlick中的click:2. srcElement/target:事件源,就是发生事件的 ...
- js中两个对象的比较
代码取自于underscore.js 1.8.3的isEqual函数. 做了一些小小的修改,主要是Function的比较修改. 自己也加了一些代码解读. <!DOCTYPE html> & ...
- MVC中处理Json和JS中处理Json对象
MVC中处理Json和JS中处理Json对象 ASP.NET MVC 很好的封装了Json,本文介绍MVC中处理Json和JS中处理Json对象,并提供详细的示例代码供参考. MVC中已经很好的封装了 ...
- js中的json对象详细介绍
JSON一种简单的数据格式,比xml更轻巧,在JavaScript中处理JSON数据不需要任何特殊的API或工具包,下面为大家详细介绍下js中的json对象, 1.JSON(JavaScript Ob ...
- JavaScript -- 时光流逝(五):js中的 Date 对象的方法
JavaScript -- 知识点回顾篇(五):js中的 Date 对象的方法 Date 对象: 用于处理日期和时间. 1. Date对象的方法 <script type="text/ ...
- JavaScript -- 时光流逝(四):js中的 Math 对象的属性和方法
JavaScript -- 知识点回顾篇(四):js中的 Math 对象的属性和方法 1. Math 对象的属性 (1) E :返回算术常量 e,即自然对数的底数(约等于2.718). (2) LN2 ...
- JavaScript -- 时光流逝(三):js中的 String 对象的方法
JavaScript -- 知识点回顾篇(三):js中的 String 对象的方法 (1) anchor(): 创建 HTML 锚. <script type="text/javasc ...
- js中 给json对象添加属性和json数组添加元素
js中 给json对象添加新的属性 比如现在有一个json对象为jsonObj,需要给这个对象添加新的属性newParam,同时给newParam赋值为pre.做法如下: var obj={ &quo ...
随机推荐
- vue项目中vux的使用
vux VUX 是基于 WeUI 和 Vue.js 的 移动端 UI 组件库,提供丰富的组件满足移动端(微信)页面常用业务需求. 在vue-cli中使用步骤如下: 1.安装: npm i vux -S ...
- arcgis api 3.x for js 地图加载多个 SHP 图层压缩以及 json 文件展示(附源码下载)
前言 关于本篇功能实现用到的 api 涉及类看不懂的,请参照 esri 官网的 arcgis api 3.x for js:esri 官网 api,里面详细的介绍 arcgis api 3.x 各个类 ...
- Oracle AWR报告详细分析--比较详细
https://blog.csdn.net/demonson/article/details/79474133
- NTSTATUS Values
By combining the NTSTATUS into a single 32-bit numbering space, the following NTSTATUS values are de ...
- 关于项目中ajax 操作 原生项目遇到的问题
单选框动态赋值 $('input[name=pszt][value='+val+']').attr("checked",true); 置顶的几种方式 window.scrollTo ...
- Javascript高级编程学习笔记(95)—— WebGL(1) 类型化数组
WebGL webgl 是针对 canvas 的 3D上下文,与其它Web技术不同,WebGL并非是W3C制定的标准,而是由 Khronos Group 制定的. 类型化数组 WebGL所涉及的复杂运 ...
- Linux命令大全汇总,让你工作更有效率!
基础命令 useradd:用户名 -m:创建新用户 passwd 用户名:为用户设置密码(当创建新用户后,就需要设置密码) whoami:查看当前用户名 exit :退出当前用户,返回之前切换过来的用 ...
- Redis常用数据类型和事物以及并发
Redis数据类型 基本类型(String int): 如 set key value .get key 等 所有命令都是按照 key value keys * 可以将全部数据列出,其中后面的 &qu ...
- MySQL系列--4.使用Python3访问数据库
1.安装MySQL驱动 pip install mysql-connector 安装完成后进入命令行模式,导入驱动,如果不报错,说明安装成功 Python 3.6.7 (default, Oct 22 ...
- 跟王思聪热狗图一样大热的Redis,还不赶紧来Get一下?
前言 不禁喊出一句ig牛逼!哈哈哈 这个话题是不是有点过时了?但说到Redis,真的是被强行灌输的,到处都会被安利Redis,吓得只会mysql和oracle的我,赶紧去get一波.. 数据库种类 关 ...