手写深拷贝

手写深拷贝

前言: 需要先了解 JS 的数据类型

一、浅拷贝、深拷贝区别

浅拷贝会创建一个新的对象,新对象有着与原始对象相同的属性值,如果

  • 属性是基本类型,拷贝的就是基本类型的值
  • 属性是引用类型,拷贝的就是内存地址(原对象地址改变,新对象也会随之改变,新对象地址改变,也会影响原对象

请添加图片描述

深拷贝会创建一个新对象,拷贝原始对象的所有内容,新对象是在内存中开辟新区域,并不共用原始对象的对象地址

  • 属性是基本类型,拷贝基本类型本身,并在内存中新开地址存储
  • 属性是引用类型,拷贝引用类型本身,并在内存中新开地址存储(原对象修改不会影响新对象,新对象修改也不会影响原对象

请添加图片描述

二、实践深拷贝

(1)最简版本

JSON.parse(JSON.stringify()); // 对无法序列化、引用类型、函数、循环引用的原对象无效

(2)基础版本

思路
  • 如果原对象是基本类型,直接返回基本类型本身即可
  • 如果原对象是引用类型,则创建新对象,并遍历原对象,并将其每个属性都执行深拷贝之后,依次添加到新对象
实践
// 测试数据 obj
let target = {a1: "stringData", // stringa2: 1234567, // numbera3: ["111", 222, { childName: "childName Data" }], // Arraya4: {info: "我是旧的 info 111",}, // objecta5: undefined, // undefineda6: null, // nulla7: 999n, // BigInta8: Symbol("foo"), // Symbol
};// 深拷贝方法
function clone(target) {if (typeof target == "object") {let cloneTarget = Array.isArray(target) ? [] : {};for (const key in target) {cloneTarget[key] = clone(target[key]);}return cloneTarget;} else {return target;}
}let res = clone(target); // 深拷贝
// let res = target; // 浅拷贝
target.a4.info = "我是新的 info 222";console.log(res, "res"); // 采用深拷贝 target.info.detail 结果 我是旧的 info 111 ,不会因为原对象改变而改变

(3)循环引用

target.target = target; // 循环引用
let res = clone(target); // 直接调用基础版本的 clone 方法
console.log(res, "res"); // 结果 Uncaught RangeError: Maximum call stack size exceeded 直接溢出

问题原因:对象存在循环引用,也就是对象的属性引用了自身,所以我们需要新开辟存储空间解决这个问题

思路
  • 开辟新的存储空间,存储原对象和新对象的对应关系
  • 拷贝对象前,先去存储空间中查找,是否已经拷贝过(来避免循环引用问题)
    • 有的话直接返回
    • 没有的话,继续拷贝
实践
/*** target 待拷贝的原对象* map 存储原对象与新对象的关系*/
function clone(target, map = new Map()) {if (typeof target == "object") {let cloneTarget = Array.isArray(target) ? [] : {};// 存储空间中有,直接返回if (map.get(target)) {return map.get(target);}// 每个待拷贝对象都在 map 里面存一下map.set(target, cloneTarget);for (const key in target) {// 把 map 带上递归cloneTarget[key] = clone(target[key], map);}return cloneTarget;} else {return target;}
}

请添加图片描述

function funcName(target, map = new Map())
这种写法表示, 第二个参数 map, 调用时有传入就用传入的,没有传入就新建一个空 map

请添加图片描述

(4)多种数据类型

思路
  • 将数据分为 2 类,分别做不同的拷贝
    • 可以继续遍历的类型(如 object、array、Map 等等)
    • 不可以继续遍历的类型 (如 Boolean、Number、String、Date、Error 等)
  1. 获取数据类型

Object.prototype.toString.call():能判断所有原始数据类型,包括 Error 对象,Date 对象 等,更多数据类型判断方式见1. 数据类型

function getDataType(target) {return Object.prototype.toString.call(target).slice(8, -1); //Array,Object,Function,String,Null,Undefined,Number,Symbol
}
  1. 分类进行拷贝
function clone(target, map = new Map()) {if (typeof target !== "object") {return target;}if (map.get(target)) {return map.get(target);}let result = {};if (getDataType(target) === "Array") {result = [];}console.log(getDataType(target));// 防止循环引用map.set(target, result);for (const key in target) {// 保证 key 不是原型属性if (Object.hasOwn(target, key)) {result[key] = clone(target[key], map);console.log(result[key], " console.log(result[key])");}}return result;
}let res = clone(target);target.a2 = null;
target.a4.info = "我是新的 info 222";
target.a3[2].childName = "我是新的 childName";
target.a7 = 1n;console.log(res, "copy res");
console.log(target, "target");

请添加图片描述

hasOwnProperty 已经逐渐废弃,官方建议使用 Object.hasOwn()
原型属性(prototype 属性)
实例属性(自身属性)

三、参考文献

  • https://juejin.cn/post/7061588533214969892
  • https://juejin.cn/post/6844903929705136141
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn
  • https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

四、结束

实际开发还是建议

  • 简单的:直接最简版本
  • 稍微复杂的:直接上 loadah

不要折磨自己了

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法——二分查找

二分算法简介&#xff1a; 二分查找算法只适用于数组有序的情况&#xff1f;&#xff08;只要数组中存在某种规律就可以用&#xff09;模版&#xff1a; 朴素的二分模版查找左边界的二分模版查找右边界的二分模版 朴素二分模版 while(left < right){int mid left (right-l…

Qt之QGraphicsView —— 笔记1:绘制简单图元(附完整源码)

效果 相关类介绍 QGraphicsView类提供了一个小部件,用于显示QGraphicsScene的内容。QGraphicsView在可滚动视口中可视化。QGraphicsView将滚动其视口,以确保该点在视图中居中。 QGraphicsScene类 提供了一个用于管理大量二维图形项的场景。请注意,QGraphicsScene没有自己的视…

【Openstack Train】十六、swift安装

OpenStack Swift是一个分布式对象存储系统&#xff0c;它可以为大规模的数据存储提供高可用性、可扩展性和数据安全性。Swift是OpenStack的一个核心组件&#xff0c;它允许用户将大量的数据存储在云上&#xff0c;并且可以随时访问、检索和管理这些数据。 Swift的设计目标是为了…

Meta开源最大多模态视频数据集—Ego-Exo4D

社交、科技巨头Meta联合15所大学的研究机构&#xff0c;经过两年多的努力发布了首个多模态视频训练数据集和基础套件Ego-Exo4D&#xff0c;用于训练和研究AI大模型。 据悉&#xff0c;该数据集收集了来自13个城市839名参与者的视频,总时长超过1400小时,包含舞蹈、足球、篮球、…

网络通信的流程,浏览器地址?

1.没有交换机的通信 在一个机房内,有两台电脑相互需要通信 假设现在有三台电脑: 随着电脑的增加,线的数量也在增加,因此显得很臃肿&#xff0c;次数交换机诞生&#xff0c;很好的解决了这一方面&#xff0c; 交换机不需要进行多条线的连接: 通过给设备分配,ip地址来实现局域网…

掌握终端,尽在ZOC for Mac – 最强大的终端仿真器!

在数字时代&#xff0c;终端仿真器是专业人士和开发者必备的工具之一。而ZOC for Mac将为您提供无与伦比的终端体验&#xff0c;助力您更轻松地管理远程连接、维护服务器和进行编程任务。 ZOC for Mac的卓越功能&#xff1a; 多协议支持&#xff1a;ZOC支持Telnet、SSH、SSH2、…

个人测试面试问题总结

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 &#x1f4d1;设计软件测试用例的方…

如何选择一款安全可靠的跨网安全数据交换系统?

随着网络和数据安全的重视程度增加&#xff0c;为了有效地保护内部的核心数据资产&#xff0c;普遍会采用内外网隔离的策略。像国内的政府机构、金融、能源电力、航空航天、医院等关乎国计民生的行业和领域均已进行了网络的隔离&#xff0c;将内部划分成不同的网段&#xff0c;…

体育场找座位 - 华为OD统一考试(C卷)

OD统一考试&#xff08;C卷&#xff09; 分值&#xff1a; 100分 题目描述 在一个大型体育场内举办了一场大型活动&#xff0c;由于疫情防控的需要&#xff0c;要求每位观众的必须间隔至少一个空位才允许落座。现在给出一排观众座位分布图&#xff0c;座位中存在已落座的观众&…

速达软件全系产品任意文件上传漏洞

声明 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 1. 速达软件产品简介 速达软件专注中小企业管理软件,产品涵盖进销存软…

智能监控/安防监控视频平台EasyCVR下级更新目录表出现离线情况的两种解决方案

GB28181安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备…

DataGrip连接虚拟机上Docker部署的Mysql出错解决

1.1 首先判断CentOS的防火墙&#xff0c;如果开启就关闭 //查看防火墙状态 systemctl status firewalld //关闭防火墙systemctl stop firewalld.service//关闭防火墙开机自启systemctl disable firewalld.service而后可以打开DataGrip连接了&#xff0c;如果连接不上执行如下…

vue项目中添加刷新的按钮

刷新功能 点击导航的刷新按钮&#xff0c;刷新下方主体内容&#xff0c;我这边的项目分为左-上-下结构&#xff0c;上边为tabbar组件&#xff0c;下边为main组件&#xff0c;点击刷新整个流程是刷新按钮&#xff0c;去访问它父组件tabbar的兄弟组件main&#xff0c;使main组件…

从零开始学习 JS APL(七):实例解析关于京东案例头部案例和放大镜效果!!

大家好关于JS APl 知识点已经全部总结了&#xff0c;第七部部分全部都是案例部分呢&#xff01;&#xff01;&#xff08;素材的可以去百度网盘去下载&#xff01;&#xff01;&#xff01;&#xff09; 目录 前言 一、个人实战文档 放大镜效果 思路分析&#xff1a; 关于其它…

新手管理者有哪些需要学习的内容?

作为新手管理者&#xff0c;需要学习的内容非常多。以下是一些重要的学习内容&#xff1a; 1. 领导力和管理技能&#xff1a;作为管理者&#xff0c;首先要学习如何有效地领导和管理团队。这包括学习如何激励员工、制定目标和计划、分配任务、解决冲突等。管理者需要具备良好的…

亚马逊云科技re:Invent大会:RAG技术赋能企业AI应用的新纪元

在最新一届re:Invent大会中&#xff0c;亚马逊云科技的数据和人工智能副总裁Swami Sivasubramanian博士提出了一系列AI产品&#xff0c;其中RAG技术成为了企业构建生成式AI应用的重要选择。这种技术的实质是将向量数据库与大语言模型相结合&#xff0c;赋予大模型记忆的能力&am…

LangChain的函数,工具和代理(五):Tools Routing

关于langchain的函数、工具、代理系列的博客我之前已经写了四篇&#xff0c;还没有看过的朋友请先看一下&#xff0c;这样便于对后续博客内容的理解&#xff1a; LangChain的函数&#xff0c;工具和代理(一)&#xff1a;OpenAI的函数调用 LangChain的函数&#xff0c;工具和代…

2023最全的Web自动化测试介绍(建议收藏)

做测试的同学们都了解&#xff0c;做Web自动化&#xff0c;我们主要用Selenium或者是QTP。 有的人可能就会说&#xff0c;我没这个Java基础&#xff0c;没有Selenium基础&#xff0c;能行吗&#xff1f;测试虽然属于计算机行业&#xff0c;但其实并不需要太深入的编程知识&…

C++模板初阶

文章目录 泛型编程函数模板格式模板调用的是同一个函数吗&#xff1f;模板的实现原理T不明确模板实例化的函数和普通函数 类模板类模板写法类模板用法 注意事项 泛型编程 假如我们要写一个两数交换的函数&#xff0c;按我们之前学的知识&#xff0c;我们会这样。 void Swap(i…

微信小程序基础bug

1.苹果11手机小程序请求数据不显示 设置-》隐私-》分析与改进-》开启 ”与开发者共享“ 2.<navigator>组件回退delta不成功 tabBar 页面是不能实现后退的效果的. 因为, 当我们跳转到 tabBar 页面&#xff0c;会关闭其他所有非tabBar 页面,所以当处于 tabBar 页面时, 无…