深拷贝浅拷贝 JS代码实现

文章目录

  • JS数据类型
  • 深拷贝 & 浅拷贝
    • 赋值和浅拷贝的区别
    • 浅拷贝(Shallow Copy)
      • 代码实现
    • 深拷贝(Deep Copy)
      • 代码实现
  • Map & WeakMap
    • 示例 WeakMap 和垃圾回收
    • weakmap处理循环引用
  • typepf & instanceof

JS数据类型

基本数据类型

Null、Undefined、Symbol(ES6) 、Boolean、String、Number、BigInt(ES2020 表示大于2^53 - 1的整数。)

引用数据类型

object、array、function

深拷贝 & 浅拷贝

要用到的 JS 知识点: for in, hasOwnProperty

深拷贝和浅拷贝是两种不同的对象复制方式,它们的区别主要在于复制对象时是否递归复制对象的属性值。

赋值和浅拷贝的区别

把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

浅拷贝(Shallow Copy)

浅拷贝仅复制对象的一级属性,如果属性值是引用类型,则复制的是引用地址,而不是实际的对象。这意味着,如果原对象的属性值是一个对象,那么拷贝后的对象和原对象共享同一个引用地址。

实现方法:

  • 使用 Object.assign() 方法。
  • 使用展开运算符 ...
  • 使用 Array.prototype.slice() 方法(对于数组)。
let original = { a: 1, b: { c: 2 } };
let shallowCopy = Object.assign({}, original);// 修改浅拷贝对象的嵌套属性
shallowCopy.b.c = 3;console.log(original.b.c); // 输出 3,原对象也被修改了

原对象有 a b c属性,但是其中属性c是引用类型,所以复制对象一级属性的时候,复制的是引用地址

let originalArray = [1, 2, 3, 4, 5];
let copiedArray = originalArray.slice(); // 创建一个原数组的浅拷贝// 修改拷贝数组不会影响原数组
copiedArray[0] = 'changed';
console.log(originalArray); // 输出: [1, 2, 3, 4, 5]
console.log(copiedArray);  // 输出: ['changed', 2, 3, 4, 5]

代码实现

遍历原对象的所有属性,赋值给新开辟的对象空间

let obj1 = {name: '李三',age: 8,friends: ['张三','王五','哈哈']
}
let newobj = shallowCopy(obj1)function shallowCopy(srcObj) {let newObjtmp = {}for(let prop in srcObj) {console.log(prop); // name age friendsif(srcObj.hasOwnProperty(prop)) {newObjtmp[prop] = srcObj[prop]}}return newObjtmp
}console.log('newObj = ', newobj);
//浅拷贝只能复制一级属性,且如果一级属性是引用类型,只复制地址obj1.friends[0] = '更改'
console.log(newobj.friends); // ['更改', '王五', '哈哈']

深拷贝(Deep Copy)

深拷贝会递归复制对象的所有层级,创建一个新的对象,并且复制原对象的所有属性值。如果属性值是引用类型,则为这个属性值创建一个新的实例。

深拷贝可以通过以下几种方式实现:

  • 使用 JSON.stringify()JSON.parse() 方法(但有局限性,例如不能复制函数、undefined、循环引用等)。
  • 使用第三方库,如 Lodash 的 _.cloneDeep() 方法。
  • 手动实现深拷贝函数。
let original = { a: 1, b: { c: 2 } };
let deepCopy;// 使用 JSON 方法实现深拷贝(有局限性)
deepCopy = JSON.parse(JSON.stringify(original));// 修改深拷贝对象的嵌套属性
deepCopy.b.c = 3;console.log(original.b.c); // 输出 2,原对象未被修改

lodash

npm install lodashimport _ from 'lodash';
// 或者 
import { cloneDeep } from 'lodash';var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = cloneDeep(objects);
console.log(deep[0] === objects[0]); // 输出 false,证明是深拷贝

代码实现

递归拷贝

var obj = {   // 原数据,包含字符串、对象、函数、数组等不同的类型name: "test",main: {a: 1,b: 2},fn: function(){console.log('This is a function');},friends: [1, 2, 3, [22, 33]]
}function deepCopy(obj) {let newObj = null;// typeof(obj)=object 有三种情况 null object arrayif(typeof(obj) == 'object' && obj != null) {newObj = obj instanceof Array? []:{}for(let i in obj) {newObj[i] = deepCopy(obj[i]) //递归调用}} else {//函数的情况newObj = obj}return newObj
}var obj2 = deepCopy(obj)
obj2.name = '修改成功'
obj2.main.a = 100
console.log(obj)
console.log(obj2)
  • 问题:

    函数没有深拷贝、循环引用问题、特殊对象没有处理(Date)、Symbol BigInt没有处理

weakmap

  • 循环引用可以用Map、WeakMap处理

在深拷贝函数中,可以使用 WeakMap 来存储已经拷贝过的对象,避免循环引用导致的无限递归。

function deepCopy(target, h = new WeakMap()) {// 1. 判断 target 是否为对象类型(包括数组)if (typeof target === 'object') {// 2. 检查 WeakMap 中是否已经存在该对象,防止循环引用if (h.has(target)) return h.get(target); // 3. 如果是数组,创建一个空数组,否则创建一个空对象(不带原型的对象)const newTarget = Array.isArray(target) ? [] : Object.create(null);// 4. 把当前对象存入 WeakMap,防止后续遇到循环引用h.set(target, newTarget);// 5. 遍历对象的所有键,递归拷贝每一个属性for (const key in target) {newTarget[key] = deepCopy(target[key], h);}// 6. 返回深拷贝的新对象return newTarget;} else {// 7. 如果 target 不是对象(如字符串、数字、布尔值等),直接返回return target;}
}
  • 解析语句 if (h.has(target)) return h.get(target);

h 是一个 WeakMap,用于记录已经被复制过的对象。

let obj = { name: "Alice" };
obj.self = obj; // 形成了循环引用let copy = deepCopy(obj);当 deepCopy 遍历到 obj.self 时,由于 self 又指向 obj 自己,函数会再次尝试拷贝 obj,这样就陷入了无限递归。使用 WeakMap,在第一次拷贝 obj 时,obj 和它的拷贝对象会被存入 WeakMap。当再次遇到 obj 时,h.has(obj) 会返回 true,直接通过 h.get(obj) 获取已经拷贝的对象,从而避免重复拷贝和无限递归。

Map & WeakMap

Map:键可以是任意类型,包括对象、函数、基本类型(例如字符串、数字等)。

WeakMap:键必须是对象,而且这些对象是弱引用的(Weak References)。这意味着,如果一个对象不再有其他引用(除了作为 WeakMap 的键),该对象可以被垃圾回收,WeakMap 不会阻止垃圾回收器回收这些对象。

示例 WeakMap 和垃圾回收

let wm = new WeakMap();let user = { name: "Alice" };
wm.set(user, "User data for Alice");// 现在 WeakMap 里有一对键值对:
// user (对象) -> "User data for Alice"// 我们仍然可以访问 `user`
console.log(wm.get(user)); // 输出: "User data for Alice"// 如果我们移除了对 `user` 的引用:
user = null;// 由于 WeakMap 对 `user` 是弱引用,
// 一旦 `user` 没有其他引用,它将被垃圾回收。
// 因此 `wm` 中的这个键值对会被自动删除。

解释:

  1. 弱引用:在代码中,WeakMap 对键 user 对象的引用是弱引用。这意味着如果没有其他地方引用这个对象(比如我们将 user 设置为 null),垃圾回收器(GC)将会自动清理掉这个对象以及它在 WeakMap 中的键值对。
  2. 垃圾回收:在将 user 设为 null 后,user 对象没有其他引用点,因此它会被垃圾回收器标记为可回收。当垃圾回收发生时,WeakMap 中与该对象相关的键值对也会被自动移除。
  3. WeakMap 优势:这样做的好处是,当我们不再需要某个对象时,WeakMap 不会阻止垃圾回收器回收它。这使得 WeakMap 非常适合用作缓存或与对象相关的临时数据存储,确保这些临时数据不会阻止相关对象的回收。

weakmap处理循环引用

循环引用是什么?循环引用(Circular Reference)是指两个或多个对象相互引用,形成一个闭环。这在 JavaScript 等语言中尤其常见,因为对象可以通过引用共享。

let obj1 = {};
let obj2 = {};obj1.ref = obj2;
obj2.ref = obj1;obj1 和 obj2 通过相互引用形成了一个循环。如果没有其他引用指向 obj1 和 obj2,理论上它们应该被垃圾回收。但有些垃圾回收算法(例如早期的引用计数法)会误以为它们仍然被引用,从而无法回收它们,导致内存泄漏。

在进行对象的深拷贝时,如果存在循环引用,可能会导致无限递归,最终导致栈溢出错误。

  • 例子:使用 WeakMap 解决循环引用
let wm = new WeakMap();let obj1 = { name: "object 1" };
let obj2 = { name: "object 2" };wm.set(obj1, obj2); // obj1 是键,obj2 是值
wm.set(obj2, obj1); // obj2 作为键,obj1 作为值// 现在 obj1 和 obj2 形成了一个相互引用的循环
console.log(wm.get(obj1)); // 输出: { name: "object 2" }
console.log(wm.get(obj2)); // 输出: { name: "object 1" }// 如果我们将 obj1 和 obj2 都设为 null,WeakMap 不会阻止它们被垃圾回收
obj1 = null;
obj2 = null;// 在没有其他引用时,这两个对象都会被垃圾回收,即使 WeakMap 中仍然有它们的键

typepf & instanceof

  • typeof 操作符不会对 null 进行特殊处理,它将 null 视为对象。
  • typeof 不能区分数组和普通对象,两者都返回 "object"

在这里插入图片描述

  • instanceof

在 JavaScript 中,instanceof 是一个二元操作符,用于测试构造函数的 prototype 属性是否出现在某个实例对象的原型链上。如果出现在原型链上,则表达式返回 true,表示该实例是该构造函数的实例;如果不在,则返回 false

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1551528.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

满填充透明背景二维码生成

前几天项目上线的时候发现一个问题:通过Hutool工具包生成的二维码在内容较少时无法填满(Margin 已设置为 0)给定大小的图片。因此导致前端在显示二维码时样式异常。 从图片中我们可以看到,相同大小的图片,留白内容是不一样的。其中上半部分…

dwceqos网络驱动性能优化

文章介绍 本文会介绍优化QNX系统下io-pkt-v6-hc驱动模块cpu loading过高问题,经过优化可以降低约一半的cpu loading. 问题背景 激光雷达通过以太网发送数据到ADAS域控中,测试发现在激光功能激活的情况下,会出现比较明显的网络丢帧现象。 …

平安养老险深圳分公司积极开展“金融教育宣传月”活动,展现金融为民新风尚

2024年9月,平安养老险深圳分公司以“金融为民谱新篇,守护权益防风险”为主题,正式启动2024年“金融教育宣传月”活动,通过多样化开展进乡村、进商圈、进企业等宣传教育活动,将金融消保知识送达广大消费者身边&#xff…

光通信——PON技术

PON网络结构 PON(Passive Optical Network,无源光网络)系统的基本组成包括OLT(Optical Line Terminal,光线路终端)、ODN(Optical Distribution Network,光分配单元)和ON…

数据结构——队列的基本操作

前言 介绍 🍃数据结构专区:数据结构 参考 该部分知识参考于《数据结构(C语言版 第2版)》24~28页 🌈每一个清晨,都是世界对你说的最温柔的早安:ૢ(≧▽≦)و✨ 目录 前言 1、队列的基本概念…

Oracle 闪回版本(闪回表到指定SCN)

1.创建目录 mkdir /u01/app/oracle/flash 2.配置FRA alter system set db_recovery_file_dest_size15G; alter system set db_recovery_file_dest/u01/app/oracle/flash; 3.设置闪回参数--确保可以闪回48h内的数据库 alter system set db_flashback_retention_target2880; 4…

中关村环球时尚产业联盟 东晟时尚产业创新中心成立

2024年9月6日,中关村环球时尚产业联盟与东晟时尚创新科技(北京)有限公司于中关村科技园东城园举行了隆重的战略合作签约仪式。 中关村科技园东城园领导发表了致辞,并表示东城区作为首都北京的核心区域,拥有深厚的历史…

SW - 装配图旋转到一个想要的正视图

文章目录 SW - 装配图旋转到一个想要的正视图概述笔记将装配图旋转到自己想要的视图的方法保存当前视图选择自己保存的视图END SW - 装配图旋转到一个想要的正视图 概述 在弄装配图。 如果按照SW默认的视图,Y方向是反的。 原因在于我画零件图时,方向就…

从“抄袭”到“原创”:5个超实用的论文降重技巧!

AIPaperGPT,论文写作神器~ https://www.aipapergpt.com/ 每当写完一篇论文,松了一口气准备庆祝时,突然想到还有一个名叫“查重”的终极大Boss等着你,瞬间心情从云端跌入谷底。 是不是你? 很多同学在提交之前&#…

fatfs API使用手册

配置 /*---------------------------------------------------------------------------/ / Configurations of FatFs Module /---------------------------------------------------------------------------*/#define FFCONF_DEF 80286 /* Revision ID *//*---------------…

Spring IoC笔记

目录 1.什么是 IoC? 2.IoC类注解(五大注解) 2.1那为什么要这么多类注解? 2.2五大注解是不是可以混用? 2.3程序被spring管理的条件是? 3.bean对象 3.1Bean 命名约定 3.2获取bean对象 4.⽅法注解 B…

业绩由盈转亏,全面冲刺大模型的360值得期待吗?

在中国互联网市场上,360无疑是一家大家家喻户晓的公司,从安全软件起家,360的服务已经延展到了市场的方方面面,就在最近360的财报正式公布,很多人都在问360的财报该怎么看?全面冲刺大模型的360值得我们期待吗…

uniapp中uni.request的统一封装 (ts版)

文章目录 前言一、我们为什么要去封装?二、具体实现1.创建一个请求封装文件:2.封装 uni.request:3.如何去使用? 总结 前言 在uniapp中如何去更简洁高效的发送我们的请求,下面就介绍了uni.request()二次封装。 一、我们…

目标检测应用场景—数据集【NO.37】纺织物缺陷检测数据集

写在前面:数据集对应应用场景,不同的应用场景有不同的检测难点以及对应改进方法,本系列整理汇总领域内的数据集,方便大家下载数据集,若无法下载可关注后私信领取。关注免费领取整理好的数据集资料!今天分享…

AI大模型面试大纲

大纲 1. 介绍和背景 自我介绍(5分钟) 了解候选人的教育背景、工作经历和对大模型架构的兴趣。 2. 基础理论和概念(30分钟) 机器学习基础 解释基本概念,如监督学习、无监督学习和强化学习。 讨论不同的模型类型&#xf…

【Iceberg分析】调研Iceberg中表的原地演变

调研Iceberg中表的原地演变 文章目录 调研Iceberg中表的原地演变原生非分区表文件关系图表的原地演变之表schema演变新增字段new_column文件关系变化图为新增字段写入数据文件关系变化图删除新增字段文件关系变化图新增字段new_column2文件关系变化图删除数据文件关系变化图 原…

uniapp学习(003-1 vue3学习 Part.1)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战,开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第11p-第p14的内容 文章目录 vue3使用介绍插值表达式例子时间戳随机数输出函数的值 ref响应式数据变量v-bind 绑…

Python入门:深入了解__init__.py 文件(如何实现动态导入子模块)

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒📝 `__init__.py` 的作用示例:📝 如何编写 `__init__.py`1. 空的 `__init__.py`2. 导入子模块3. 初始化代码4. 动态导入子模块📝 编写 `__init__.py` 的技巧和注意事项⚓️ 相关链接 ⚓️📖 介绍 📖 在…

大数据-155 Apache Druid 架构与原理详解 数据存储 索引服务 压缩机制

点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…

永不失联!遨游双卫星三防手机成为高效应急关键所在

今年9月被戏称为“台风月”,台风“摩羯”、“贝碧嘉”以及热带气旋“普拉桑”接连来袭,极端天气不仅导致了电力中断、道路损毁,更使得传统的通信网络遭受重创,给应急通信保障工作带来了极大的压力。面对“三断”的实战难题&#x…