详解JavaScript对象的深浅复制


Posted in Javascript onMarch 30, 2017

前言

从层次上来看,对象的复制可以简单地分为浅复制和深复制,顾名思义,浅复制是指只复制一层对象的属性,不会复制对象中的对象的属性,对象的深复制会复制对象中层层嵌套的对象的属性。

在复制对象时,除了要复制对象的属性外,还要兼顾到是否保留了对象的constructor属性,是否对每一种数据类型(JavaScript常见的数据类型有String,Number,Boolean,Data,RegExp,Array,Funtion,Object)都实现正确的复制。项目中,我们可以根据实际情况,决定需要实现什么样程度的复制。

本文是我在复制对象方面的一些心得总结,由浅复制到深复制,由只复制简单属性到复制Function,RegExp等复杂属性,层层递进。如有陈述不当之处,烦请指出,不胜感激。

正文

浅复制

浅复制只会依次复制对象的每一个属性,不会对这些属性进行递归复制。下面是一个简单的浅复制实现。

//对象浅复制        
function shadowCopy(obj){
 if(typeof obj !== 'object') return obj;
 for(var prop in obj){
  if(obj.hasOwnProperty(prop)){
  newObj[prop] = obj[prop];
  }
 }
 return newObj;
 }

仔细观察,不难发现上述方法的缺陷:

1.不能正确实现数组的浅复制

2.复制操作丢失了对象的constructor属性

好,我们现在已经发现了问题所在,只需针对性地解决,一个还算完美的浅复制对象的方法就诞生了!

//对象浅复制
 function shadowCopy(obj){
  if(typeof obj !== 'object') return ;
  var newObj;
  //保留对象的constructor属性
  if(obj.constructor === Array){
  newObj = [];
  } else {
  newObj = {};
  newObj.constructor = obj.constructor;
  }
  for(var prop in obj){
  if(obj.hasOwnProperty(prop)){
   newObj[prop] = obj[prop];
  }
  }
  return newObj;
 }

浏览器中测试一下:

var arr1 = [0,1,2];
 console.log(arr1);
 console.log(shadowCopy(arr1));
 var arr2 = [0,1,2,[3,4,5]],
 arr2Copy = shadowCopy(arr2);
 console.log(arr2);
 console.log(arr2Copy);
 arr2Copy[3][0] = 6;
 console.log(arr2[3][0]); //6

详解JavaScript对象的深浅复制

Good! 可以正确实现数组复制和并且保留constructor了,但细心的你一定发现了,浅复制后的对象的 arr2Copy[3] 和 arr2[3] 指向的是一个对象,改变其中一个,同时也会改变另一个。我们想要实现的是 复制,但这并不是复制呀!
这是浅复制的一个弊端所在,接下让我们看看深复制是怎样解决这个问题的。

深复制

深复制需要层层递归,复制对象的所有属性,包括对象属性的属性的属性....(晕~)
如果只是需要简单地复制对象的属性,而不用考虑它的constructor,也不用考虑函数,正则,Data等特殊数据类型,那这里有一个深复制的小trick,两行代码即可:

function deepCopy(obj){
 if(typeof obj !== "object"){ return ;}
 var str = JSON.stringify(obj);
 return JSON.parse(str);
}

大多数情况下,上面的就可以满足要求了,但一些时候,我们需要把函数,正则等特殊数据类型也考虑在内,或者当前环境不支持JSON时,上面的方法也就不适用了。这时,我们可以通过递归来实现对象的深层复制,如下:

function deepCopy(obj){
 if(typeof obj !== "object"){ return ;}
 var newObj;
 //保留对象的constructor属性
 if(obj.constructor === Array){
 newObj = [];
 } else {
 newObj = {};
 newObj.constructor = obj.constructor;
 }
 for(var prop in obj){
 if(typeof obj[prop] === 'object'){
  if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
  newObj[prop] = obj[prop];
  } else {
  //递归
  newObj[prop] = deepCopy(obj[prop]);
  }
 } else {
  newObj[prop] = obj[prop];
 }
 }
 return newObj;
}

先用上面的例子测试:

详解JavaScript对象的深浅复制

棒!可以正确实现多维数组的复制,再看是否能实现函数和正则的复制:

function Person(name){
 this.name = name;
 this.age = age;
 this.search = new RegExp(name);
 this.say = function(){
 console.log(this.name + "今年" + this.age + "岁了");
 }
}
var p1 = new Person("Claiyre",20),
 p2 = deepCopy(p1);
console.log(p1);
console.log(p2);
p2.age = 22;
p1.say();
p2.say();

详解JavaScript对象的深浅复制

圆满完成!!

稍加整理,我们就可以得到一个较为通用的js对象复制函数:

function deepCopy(obj){
 var newObj = obj.constructor === Array ? []:{};
 newObj.constructor = obj.constructor;
 if(typeof obj !== "object"){ 
 return ;
 } else if(window.JSON){
 //若需要考虑特殊的数据类型,如正则,函数等,需把这个else if去掉即可
 newObj = JSON.parse(JSON.stringify(obj));
 } else {
 for(var prop in obj){
  if(obj[prop].constructor === RegExp ||obj[prop].constructor === Date){
  newObj[prop] = obj[prop];
  } else if(typeof obj[prop] === 'object'){
  //递归
  newObj[prop] = deepCopy(obj[prop]);
  } else {
  newObj[prop] = obj[prop];
  }
 }
 } 
 return newObj;
}

结语

面向对象的编程语言,其核心是对象,因此深入了解对象的相关操作,纵向比较异同,对学习过程是极有好处的。

以上所述是小编给大家介绍的JavaScript对象的深浅复制,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Jquery中增加参数与Json转换代码
Nov 20 Javascript
利用onresize使得div可以随着屏幕大小而自适应的代码
Jan 15 Javascript
JavaScript获取图片的原始尺寸以宽度为例
May 04 Javascript
js不能获取隐藏的div的宽度只能先显示后获取
Sep 04 Javascript
基于Jquery代码实现手风琴菜单
Nov 19 Javascript
jQuery中text() val()和html()的区别实例详解
Jun 28 Javascript
javascript中活灵活现的Array对象详解
Nov 30 Javascript
JS使用正则实现去掉字符串左右空格的方法
Dec 27 Javascript
JS动画定时器知识总结
Mar 23 Javascript
详解在vue-cli3.0中自定css、js和图片的打包路径
Aug 26 Javascript
Vue实现PC端靠边悬浮球的代码
May 09 Javascript
vue data有值,但是页面{{}} 取不到值的解决
Nov 09 Javascript
js实现不提示直接关闭网页窗口
Mar 30 #Javascript
jquery中关于bind()方法的使用技巧分享
Mar 30 #jQuery
JavaScript实现弹出广告功能
Mar 30 #Javascript
JavaScript如何一次性展示几万条数据
Mar 30 #Javascript
ECMAScript6--解构
Mar 30 #Javascript
js图片放大镜效果实现方法详解
Oct 28 #Javascript
js a标签点击事件
Mar 30 #Javascript
You might like
php查询whois信息的方法
2015/06/08 PHP
CodeIgniter辅助之第三方类库third_party用法分析
2016/01/20 PHP
PHP下的浮点运算不准的解决方法
2016/10/27 PHP
PHP输出图像imagegif、imagejpeg与imagepng函数用法分析
2016/11/14 PHP
JS加ASP二级域名转向的代码
2007/05/17 Javascript
IE8 兼容性问题(属性名区分大小写)
2009/06/04 Javascript
javascript 进度条 实现代码
2009/07/30 Javascript
浅析JavaScript中的常用算法与函数
2013/11/21 Javascript
jQuery EasyUI之DataGrid使用实例详解
2016/01/04 Javascript
JavaScript引用类型和基本类型详解
2016/01/06 Javascript
详解Wondows下Node.js使用MongoDB的环境配置
2016/03/01 Javascript
使用JavaScript判断手机浏览器是横屏还是竖屏问题
2016/08/02 Javascript
js document.getElementsByClassName的使用介绍与自定义函数
2016/11/25 Javascript
jquery 标签 隔若干行加空白或者加虚线的方法
2016/12/07 Javascript
NodeJS 实现手机短信验证模块阿里大于功能
2017/06/19 NodeJs
jQuery实现动态生成年月日级联下拉列表示例
2019/05/11 jQuery
vue App.vue中的公共组件改变值触发其他组件或.vue页面监听
2019/05/31 Javascript
VUE实现移动端列表筛选功能
2019/08/23 Javascript
vue.js循环radio的实例
2019/11/07 Javascript
详解如何在JS代码中消灭for循环
2019/12/11 Javascript
Vue.js中Line第三方登录api的实现代码
2020/06/29 Javascript
[00:44]TI7不朽珍藏III——军团指挥官不朽展示
2017/07/15 DOTA
利用Python操作消息队列RabbitMQ的方法教程
2017/07/19 Python
python利用正则表达式排除集合中字符的功能示例
2017/10/10 Python
Python实战小程序利用matplotlib模块画图代码分享
2017/12/09 Python
对python中的argv和argc使用详解
2018/12/15 Python
matplotlib.pyplot绘图显示控制方法
2019/01/15 Python
keras自定义回调函数查看训练的loss和accuracy方式
2020/05/23 Python
世界上最大的各式箱包网络零售店:eBag
2016/07/21 全球购物
欧姆龙医疗保健与医疗产品:Omron Healthcare
2020/02/10 全球购物
机械专业毕业生自荐信
2013/11/02 职场文书
电子商务专业学生的自我鉴定
2013/11/28 职场文书
村委会贫困证明范本
2014/09/17 职场文书
交通事故死亡赔偿协议书
2014/12/03 职场文书
三严三实·严以修身心得体会
2016/01/15 职场文书
教你使用TensorFlow2识别验证码
2021/06/11 Python