JS中实现浅拷贝和深拷贝的代码详解


Posted in Javascript onJune 05, 2019

(一)JS中基本类型和引用类型

JavaScript的变量中包含两种类型的值:基本类型值 和 引用类型值,在内存中的表现形式在于:前者是存储在栈中的一些简单的数据段,后者则是保存在堆内存中的一个对象。

基本类型值

在JavaScript中基本数据类型有 String , Number , Undefined , Null , Boolean ,在ES6中,又定义了一种新的基本数据类型 Symbol ,所以一共有6种。

基本类型是按值访问的,从一个变量复制基本类型的值到另一个变量后,这两个变量的值是完全独立的,即使一个变量改变了也不会影响到第二个变量。

var str1 = '撩课';
var str2 = str1;
str2 = 'itlike';
console.log(str2); //'itlike'
console.log(str1); //'撩课'

引用类型值

引用类型值是引用类型的实例,它是保存在堆内存中的一个对象,引用类型是一种数据结构,最常用的是Object,Array,Function类型,此外还有Date,RegExp,Error等。

在ES6中提供了Set,Map2种新的数据结构。

(二)JS中如何复制引用类型的

基本类型和引用类型赋值的差异化
举个例子:在下面代码中,只修改了obj1中的name属性,却同时改变了ob1和obj2中的name属性。

var obj1 = {'name': '撩课'};
var obj2 = obj1;
obj2.name = '小撩';
console.log(obj1); // {'name': '撩课'}
console.log(obj2); // {'name': '撩课'}

当变量复制引用类型值的时候,同样和基本类型值一样会将变量的值复制到新变量上,不同的是对于变量的值,它是一个指针,指向存储在堆内存中的对象。

因为,在JS中,堆内存中的对象无法直接访问,必须要访问这个对象在堆内存中的地址,然后再按照这个地址去获得这个对象中的值。

(三)浅拷贝

在JS中,如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址;所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

下面是JavaScript提供的浅拷贝方法:

Object.assign

ES6中拷贝对象的方法,接受的第一个参数是拷贝的目标,剩下的参数是拷贝的源对象;

语法:Object.assign(target, ...sources)

var p = {
  'name': '张三',
};
var copyP = {};
Object.assign(copyP, p);
console.log(copyP);
console.log(p);

Object.assign是一个浅拷贝,它只是在根属性(对象的第一层级)创建了一个新的对象,但是如果属性的值是对象的话,只会拷贝一份相同的内存地址。

扩展运算符

利用扩展运算符可以在构造字面量对象时,进行克隆或者属性拷贝。语法如下:

var cloneObj = { ...obj };
var obj = {'name': '撩课', 'college': ['H5','JAVA','Python']}
var obj2 = {...obj};
obj.name='小撩';
//{'name': '小撩', 'college': ['H5','JAVA','Python']}
console.log(obj);
//{'name': '撩课', 'college': ['H5','JAVA','Python']}
console.log(obj2); 
obj.college.push('Go');
//{'name': '小撩', 'college': ['H5','JAVA','Python','Go']}
console.log(obj); 
//{'name': '小撩', 'college': ['H5','JAVA','Python','Go']}
console.log(obj2);

扩展运算符和Object.assign()存在同样的问题,对于值是对象的属性无法完全拷贝成两个不同对象;

但是如果属性都是基本类型的值的话,使用扩展运算符更加简洁。

(四)深拷贝

浅拷贝只在根属性上在堆内存中创建了一个新的的对象,复制了基本类型的值,但是复杂数据类型也就是对象则是拷贝相同的地址。

而深拷贝则是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

JSON.stringify

JSON.stringify()是目前开发过程中最常用的深拷贝方式,原理是把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在内存中,再用JSON.parse()反序列化将JSON字符串变成一个新的对象。

举个例子:

var obj = {
  name: '撩课',
  age: 18,
  friends: ['小花', '小黑'],
  goodF: {
    name: '小撩',
    age: 19,
    address: '上海',
    pets: [{name: '土豆'}, {name: '马铃薯'}]},
  bir: new Date()
};

var newObj = JSON.parse(JSON.stringify(obj));
obj.goodF.pets[0].name = '旺财';
console.log(newObj);
console.log(obj);

使用JSON.stringify实现深拷贝有几点要注意:

1)拷贝的对象的值中如果有函数,undefined,symbol,经过JSON.stringify()序列化后的JSON字符串中这个键值对会消失;

无法拷贝不可枚举的属性,无法拷贝对象的原型链
3)拷贝Date引用类型会变成字符串

4)拷贝RegExp引用类型会变成空对象

对象中含有NaN、Infinity和-Infinity,则序列化的结果会变成null

递归实现深拷贝

具体实现如下:

/**
 * 辅助函数, 判定是否是对象
 * @param obj
 * @returns {boolean}
 */
function isObj(obj) {
  return obj instanceof Object;
}

/**
 * 深拷贝fromObj面的所有属性/值, 到toObj对象里面
 * @param fromObj 拷贝对象
 * @param toObj  目标对象
 */
function deepCopyObj2NewObj(fromObj, toObj) {
  for (var key in fromObj) {
    if(fromObj.hasOwnProperty(key)){
      var fromValue = fromObj[key];
      // 如果是值类型,那么就直接拷贝赋值
      if (!isObj(fromValue)) {
        toObj[key] = fromValue;
      } else {
        // 如果是引用类型,那么就再调用一次这个方法,
        // 去内部拷贝这个对象的所有属性
        // fromValue是什么类型, 创建一个该类型的空对象
        var tmpObj = new fromValue.constructor;

        // console.log(tmpObj);
        // debugger;
        deepCopyObj2NewObj(fromValue, tmpObj);
        toObj[key] = tmpObj;
      }
    }
  }
}

(五)总结

1)在日常开发中一般并不需要拷贝很多特殊的引用类型,深拷贝对象使用JSON.stringify是最直接和简单的方法。

2)实现一个完整的深拷贝是非常复杂的,需要考虑到很多边界情况。对于特殊的引用类型有拷贝需求的话,建议借助第三方完整的库。

以上所述是小编给大家介绍的JS中实现浅拷贝和深拷贝的代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
网络图片延迟加载实现代码 超越jquery控件
Mar 27 Javascript
汉化英文版的Dreamweaver CS5并自动提示jquery
Nov 25 Javascript
javascript 主动派发事件总结
Aug 09 Javascript
实例代码讲解jquery easyui动态tab页
Nov 17 Javascript
利用bootstrapValidator验证UEditor
Sep 14 Javascript
javascript 内置对象及常见API详细介绍
Nov 01 Javascript
js 打开新页面在屏幕中间的实现方法
Nov 02 Javascript
jQuery Validate 数组 全部验证问题
Jan 12 Javascript
Angular之指令Directive用法详解
Mar 01 Javascript
js中获取键盘按下键值event.keyCode、event.charCode和event.which的兼容性详解
Mar 15 Javascript
angular 用拦截器统一处理http请求和响应的方法
Jun 08 Javascript
vue.js的vue-cli脚手架中使用百度地图API的实例
Jan 21 Javascript
优雅的处理vue项目异常实战记录
Jun 05 #Javascript
原生JS使用Canvas实现拖拽式绘图功能
Jun 05 #Javascript
Node.js 路由的实现方法
Jun 05 #Javascript
JS实现动态添加外部js、css到head标签的方法
Jun 05 #Javascript
JS函数动态传递参数的方法分析【基于arguments对象】
Jun 05 #Javascript
jQuery操作cookie的示例代码
Jun 05 #jQuery
JS实现从对象获取对象中单个键值的方法示例
Jun 05 #Javascript
You might like
计算2000年01月01日起到指定日的天数
2006/10/09 PHP
PHP __autoload()方法真的影响性能吗?
2012/03/30 PHP
PHP实现对图片的反色处理功能【测试可用】
2018/02/01 PHP
document.compatMode介绍
2009/05/21 Javascript
通过DOM脚本去设置样式信息
2010/09/19 Javascript
如何使用jQuery Draggable和Droppable实现拖拽功能
2013/07/05 Javascript
自定义百度分享的分享按钮
2015/03/18 Javascript
JavaScript实现时间倒计时跳转(推荐)
2016/06/28 Javascript
基于jQuery实现的查看全文功能【实用】
2016/12/11 Javascript
JS中检测数据类型的几种方式及优缺点小结
2016/12/12 Javascript
关于TypeScript中import JSON的正确姿势详解
2017/07/25 Javascript
vue单页应用在页面刷新时保留状态数据的方法
2018/09/21 Javascript
记一次vue跨域的解决
2020/10/21 Javascript
Python实现句子翻译功能
2017/11/14 Python
从CentOS安装完成到生成词云python的实例
2017/12/01 Python
Python实现判断并移除列表指定位置元素的方法
2018/04/13 Python
python中import与from方法总结(推荐)
2019/03/21 Python
详解Python 调用C# dll库最简方法
2019/06/20 Python
在Python中表示一个对象的方法
2019/06/25 Python
Python代码太长换行的实现
2019/07/05 Python
pymysql模块的使用(增删改查)详解
2019/09/09 Python
Python下应用opencv 实现人脸检测功能
2019/10/24 Python
使用pandas的box_plot去除异常值
2019/12/10 Python
python 爬虫之selenium可视化爬虫的实现
2020/12/04 Python
纯HTML5+CSS3制作生日蛋糕代码
2016/11/16 HTML / CSS
探索HTML5本地存储功能运用技巧
2016/03/02 HTML / CSS
家用个人磨皮机:Trophy Skin
2017/03/30 全球购物
党员思想汇报范文
2013/12/30 职场文书
庆元旦迎新年广播稿
2014/02/18 职场文书
出纳岗位职责范本
2015/03/31 职场文书
发票退票证明
2015/06/24 职场文书
python基础之文件处理知识总结
2021/05/23 Python
使用qt quick-ListView仿微信好友列表和聊天列表的示例代码
2021/06/13 Python
《进击的巨人》新联动CM 兵长强势出击兽巨人
2022/04/05 日漫
Python+Matplotlib图像上指定坐标的位置添加文本标签与注释
2022/04/11 Python
Win10此设备不支持接收Miracast无法投影的解决方法
2022/07/07 数码科技