侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

前端  /  管理员 发布于 5年前   253

(一)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中实现浅拷贝和深拷贝的代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

您可能感兴趣的文章:

  • JS浅拷贝和深拷贝原理与实现方法分析
  • JavaScript对象的浅拷贝与深拷贝实例分析
  • JavaScript实现浅拷贝与深拷贝的方法分析
  • JavaScript深拷贝和浅拷贝概念与用法实例分析
  • javascript深拷贝、浅拷贝和循环引用深入理解
  • JavaScript基础心法 深浅拷贝(浅拷贝和深拷贝)
  • 浅析javaScript中的浅拷贝和深拷贝
  • javascript深拷贝和浅拷贝详解
  • javascript对浅拷贝和深拷贝的详解
  • js对象浅拷贝和深拷贝详解
  • 浅谈JavaScript中面向对象的的深拷贝和浅拷贝
  • JavaScript数组深拷贝和浅拷贝的两种方法
  • Javascript 浅拷贝、深拷贝的实现代码
  • javascript二维数组和对象的深拷贝与浅拷贝实例分析
  • javascript 关于赋值、浅拷贝、深拷贝的个人理解


  • 上一条:
    优雅的处理vue项目异常实战记录
    下一条:
    详解vue父子组件关于模态框状态的绑定方案
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客