【JavaScript】浅拷贝与深拷贝
引言
浅拷贝、深拷贝是对引用类型而言的。
引用类型的变量对应一个栈区地址,这个栈区地址处存储的值是存放的真正的数据的堆区地址。
基本数据类型的变量也对应一个栈区地址,但是该地址存储的是其真正的值。
let a = b
发生了什么?
let obj2 = obj1
发生了什么?
JavaScript的数据类型:
什么是浅拷贝?
浅拷贝(shallow copy)创建的新对象拷贝的是原对象的属性的栈区地址。
图中同名变量的栈区地址相同,不同名变量的栈区地址不同。
a
和_a
、b
和_b
都是复制了原来栈区地址的值,对_a
的修改不会影响a
,对_b
的修改却会影响b
,因为它们相当于let _b = b
的关系。
什么是深拷贝?
深拷贝(deep copy)拷贝对象的堆区数据为新副本,如此新旧对象不会互相影响。
浅拷贝的方法有哪些?
1.JavaScript中对象的合并,Object.assign
本身是浅拷贝。
const originalObject = {a:1,b:{c:1}}
const shallowCopy = Object.assign({}, originalObject);
console.log(shallowCopy === originalObject);//false,比较的是栈区地址
shallowCopy.a = 2;
shallowCopy.b.c = 2;
console.log(originalObject.a);// 1
console.log(originalObject.b.c);// 2
缺陷:Object.assign
不会拷贝继承属性、不可枚举属性。
2.展开语法
let newObj = {...obj}
3.数组的cancat方法
const newArr = oldArr.concat([])
4.数组的slice方法
const newArr = oldArr.slice(start[,end]);
5.浅拷贝细致点看,是先创建一个新对象,然后将原对象的属性直接复制到新对象,所以也可以自己写一个浅拷贝函数:
function shallowCopy(obj) {if(obj === null || typeof obj !== 'object') return obj;const newObj = {};for (let key in obj) {// 会遍历原型链上的可枚举属性obj.hasOwnProperty(key) && (newObj[key] = obj[key]);}return newObj;
}
// Object.prototype.d = 1;
const obj1 = { a: 1, b: { c: 1 } };
const obj2 = shallowCopy(obj1);
obj2.a = 2;
obj2.b.c = 2;
也可以不用每次判断是否是自有属性:
function shallowCopy(obj) {if(obj === null || typeof obj !== 'object') return obj;const newObj = {};Object.getOwnPropertyNames(obj).forEach(key=>{newObj[key] = obj[key];})return newObj;
}
6.lodash库的浅拷贝方法
如何实现深拷贝?
1.JSON.stringify()
与JSON.parse()
function deepClone(obj){if(obj === null || typeof obj !== 'object') return obj;return JSON.parse(JSON.stringify(obj));
}
缺陷:
- 丢失function、undefined、Symbol这几种类型的键值对
- NaN、Infinity的值会转为null
- Date会变为字符串
- RegExp会变成空对象
- 不能拷贝不可枚举属性及原型链上的属性
- 不能解决循环引用
2.lodash库的深拷贝方法
3.手动实现深拷贝函数基础版:
function deepClone(obj) {if(obj === null || typeof obj !== 'object') return obj;const newObj = new obj.constructor();for (let key in obj) {if (obj.hasOwnProperty(key)){newObj[key] = typeof obj[key] === "object" ? arguments.callee(obj[key]) : obj[key];}}return newObj;
}
const newObj = new obj.constructor()
相比于使用{}
,保持了原型链的继承。
缺陷:
- 不能处理循环引用,可能会导致堆栈溢出
- 对
Array
、Date
、RegExp
、Map
、Set
对象的处理不好 - 不能拷贝不可枚举属性和
Symbol
类型属性
4.手动实现深拷贝函数进阶版:
//判断是否为复杂数据类型
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null);const deepClone = function(obj,hash = new WeakMap()){if(hash.has(obj)) return hash.get(obj);//如果参数为Date, RegExp, Set, Map, WeakMap, WeakSet等引用类型,则直接生成一个新的实例let type = [Date,RegExp,Set,Map,WeakMap,WeakSet];if(type.includes(obj.constructor)) return new obj.constructor(obj);//遍历传入参数所有属性描述符let allDesc = Object.getOwnPropertyDescriptors(obj);//继承原型let cloneObj = Object.create(Object.getPrototypeOf(obj),allDesc);// 获取所有 Symbol 类型键let symKeys = Object.getOwnPropertySymbols(obj);// 拷贝 Symbol 类型键对应的属性if (symKeys.length > 0) {symKeys.forEach(symKey => {cloneObj[symKey] = isComplexDataType(obj[symKey]) ? deepClone(obj[symKey], hash) : obj[symKey]})}// 哈希表设值hash.set(obj,cloneObj);//Reflect.ownKeys(obj)拷贝不可枚举属性和符号类型for(let key of Reflect.ownKeys(obj)){// 如果值是引用类型并且非函数则递归调用deepClonecloneObj[key] =(isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key],hash) : obj[key];}return cloneObj;
};
参考资料:
- JavaScript中浅拷贝和深拷贝的区别与实现
- JavaScript深拷贝和浅拷贝看这篇就够了
- 关于堆栈的讲解(我见过的最经典的)
- 深入理解js数据类型与堆栈内存
- js中深浅拷贝的实现方式(含图解原理)
- JavaScript深拷贝看这篇就行了!(实现完美的ES6+版本)
- 【JavaScript】arguments.callee的作用及替换方案
- [javascript核心-15] 手写完美深拷贝代码实现🍌
- JS中JSON序列化JSON.stringify的坑点和处理
相关文章:

【JavaScript】浅拷贝与深拷贝
引言 浅拷贝、深拷贝是对引用类型而言的。 引用类型的变量对应一个栈区地址,这个栈区地址处存储的值是存放的真正的数据的堆区地址。 基本数据类型的变量也对应一个栈区地址,但是该地址存储的是其真正的值。 let a b发生了什么? let obj…...

如何下载IEEE Journal/Conference/Magazine的LaTeX/Word模板
当你准备撰写一篇学术论文或会议论文时,使用IEEE(电气和电子工程师协会)的LaTeX或Word模板是一种非常有效的方式,它可以帮助你确保你的文稿符合IEEE出版的要求。无论你是一名研究生生或一名资深学者,本教程将向你介绍如…...

nvidia 驱动问题
https://stackoverflow.com/questions/43022843/nvidia-nvml-driver-library-version-mismatch https://zhuanlan.zhihu.com/p/643773939...

PDF编辑和OCR文字识别工具ABBYY FineReader PDF
ABBYY FineReader PDF是一款专业的OCR文字识别和PDF编辑工具,可以帮助用户更好地处理和管理PDF文档。以下是ABBYY FineReader PDF的一些特点: 1. 文字识别精准:ABBYY FineReader PDF具有强大的OCR文字识别功能,可以将PDF中的文字…...

什么是网络流量监控
随着许多服务迁移到云,网络基础架构的维护变得复杂。虽然云采用在生产力方面是有利的,但它也可能让位于未经授权的访问,使 IT 系统容易受到安全攻击。 为了确保其网络的安全性和平稳的性能,IT 管理员需要监控用户访问的每个链接以…...

ubuntu 终端 中文显示unicode码、乱码
Ubuntu默认的中文字符编码 locale命令查看 LANG 等参数是否无UTF-8等参数?比如 为空? Ubuntu默认的中文字符编码为zh_CN.UTF-8,这个可以在 /etc/environment中看到: sudo gedit /etc/environment 可以看到如下内容: P…...

作用域理解
概念:它是指对某一变量和方法具有访问权限的代码空间, 在JS中, 作用域是在函数中维护的。表示变量 或函数起作用的区域,指代了它们在什么样的上下文中执行,亦即上下文执行环境。 ES5的作用域只有两种:全局作用域和局部作用域 全局作用域 var a1; //全局作用域 function fn1(…...

Stream 流式编程创建及其常用操作方法
目录 Stream 对象如何创建 Stream 常用的操作方法 1.过滤(Filter) 2.映射(Map) 3.扁平映射(FlatMap) 4.截断(Limit) 5.跳过(Skip) 6.排序(…...

Can 通信-协议
概述 CAN 是 Controller Area Network 的缩写(以下称为 CAN),是 ISO国际标准化的串行通信协议。 在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统 被开发了出来。由于…...

rustlings本地开发环境配置
克隆自己的仓库 首先我们要在github上找到自己仓库并把它克隆到本地 git clone https://github.com/LearningOS/rust-rustlings-2023-autumn-******.git下载插件 rust-analyzer和Git Graph一个可以用来解析rust代码,另一个可以可视化管理git代码库 下载rustling…...

希尔排序:优化插入排序的精妙算法
排序算法在计算机科学中扮演着重要的角色,其中希尔排序(Shell Sort)是一种经典的排序算法。本文将带您深入了解希尔排序,包括其工作原理、性能分析以及如何使用 Java 进行实现。 什么是希尔排序? 希尔排序,…...

新能源电动汽车安全性能检测中采集车架号及BMS电池数据的难点
按照新能源电动汽车安全性能检测,必须采集到汽车的车架号及BMS电池数据做对应的评测。国内电动汽车主要以比亚迪、特斯拉、广汽埃安、五菱新能源、长安新能源、大众、理想、蔚来、哪吒等主流为主。与传统燃油车不同的是,电动汽车不用执行OBD2标准&#x…...

函数reshape(-1,)里的-1的意思
reshape函数是对narray的数据结构进行维度变换,由于变换遵循对象元素个数不变,在进行变换时,假设一个数据对象narray的总元素个数为N,如果我们给出一个维度为(m,-1)时,我们就理解为将…...

名词作形容词的用法
【名词作形容词的用法】 有考英语二的同学问我,为什么名词能修饰名词,比如spending proportion(支出比例), job satisfaction(工作满意度),之前老师不是说这种写法不能乱用么? 那我在这里简单说明一下“形…...

若依微服务部署,裸服务部署、docker部署、k8s部署
目录 前言windows 部署若依-微服务版本浏览器验证docker部署若依-微服务版本浏览器验证k8s部署若依-微服务版本浏览器验证总结 前言 环境:centos7、Win10 若依是一个合适新手部署练习的开源的微服务项目,本篇讲解Windows部署若依微服务、docker部署若依…...

【置顶】关于博客的一些公告
所谓 万事开头难,最开始的两个专栏 《微机》 和 《骨骼动作识别》 定价 29.9 ,因为: 刚开始确实比较困难,要把自己学的知识彻底搞懂讲给别人,还要 码字排版,从 Markdown 语法开始学起(这都是 花…...

Fastadmin后端表格动态展示列
前言 后端有多角色时, 往往有些表格中的列需要根据条件来根据角色身份决定是不是需要该角色查看, 为此就衍生出一个需要动态控制展示某列的需求fastadmin框架内调用的table实际上在初始化时, 可以修改columns中的visible属性来控制是否显示, 但是这个参数只能传入bool, 不能像…...

如何在ubnutu上安装docker
卸载旧版本 sudo apt-get remove docker docker-engine docker.io添加HTTPS传输软件包以及CA证书 sudo apt-get update sudo apt-get install \apt-transport-https \ca-certificates \curl \gnupg \lsb-release添加国内源以提升网速 添加软件源的GPG秘钥以确认所下载软件包…...

Mall脚手架总结(三) —— MongoDB存储浏览数据
前言 通过Elasticsearch整合章节的学习,我们了解SpringData框架以及相应的衍生查询的方式操作数据读写的语法。MongoDB的相关操作也同样是借助Spring Data框架,因此这篇文章的内容比较简单,重点还是弄清楚MongoDB的使用场景以及如何通过Sprin…...

Maven 引入外部依赖
如果我们需要引入第三方库文件到项目,该怎么操作呢? pom.xml 的 dependencies 列表列出了我们的项目需要构建的所有外部依赖项。 要添加依赖项,我们一般是先在 src 文件夹下添加 lib 文件夹,然后将你工程需要的 jar 文件复制到 …...

BS EN 12104-2023 软木地砖检测
软木地砖是指含有烧结成分的软木制成的块状砖,可用于地面覆盖物,装饰层等,具有脚感柔软舒适,防滑性能好,静音等性能,同时也其耐磨性较差,不易清洁。 BS EN 12104-2023软木地砖测试 测试项目 测…...

用Nginx搭建一个可用的静态资源Web服务器
sudo wget http://dlib.net/files/dlib-19.24.tar.bz2下载需要的文件。 sudo tar jxf dlib-19.24.tar.bz2进行解压。 sudo mkdir /nginx/dlib在nginx安装目录/nginx创建一个新的目录dlib。 配置文件nginx.conf里边的内容如下: worker_processes 1; events {…...

MAX30102心率血氧传感器
MAX30102心率血氧传感器介绍 背景基本功能基本结构基本原理采集方法直通式采集方法反射式采集方法 血氧采集原理Beer-Lambert 定理皮肤组织模型血氧测量过程AC / DC 的计算 心率采集原理 实验结果代码走读资源链接 背景 目前,基本上所有的可穿戴式设备都集成了心率…...

高效解决 TypeError : ‘ numpy._DTypeMeta‘ object is not subscriptable 问题
文章目录 问题描述解决问题 问题描述 解决问题 参考博文 打开报错位置 AppData\Roaming\Python\Python39\site-packages\cv2\typing\ 添加single-quotes,即单引号 博主说The trick is to use single-quotes to avoid the infamous TypeError: ‘numpy._DTypeMeta’…...

Hadoop作业篇(一)
一、选择题 1. 以下哪一项不属于Hadoop可以运行的模式__C____。 A. 单机(本地)模式 B. 伪分布式模式 C. 互联模式 D. 分布式模式 C. 互联模式 不属于Hadoop可以运行的模式。 Hadoop主要有四种运行模式: A. 单机(本地…...

SpringCloud中的分布式锁用法详解(Java+Redis SETNX命令)
前言: 在分布式系统中,保证数据的一致性和并发控制是至关重要的。分布式锁能够解决多个进程/线程同时访问共享资源的问题,确保只有一个进程/线程能够获得锁。本文将介绍如何使用Java和Redis实现分布式锁,并提供示例代码和注意事项…...

初学者如何选择:前端开发还是后端开发?
#开发做前端好还是后端好【话题征文】# 作为一名有多年开发经验的过来人,我认为前端开发和后端开发都有其独特的魅力和挑战。下面我将就我的个人经历和观点来分享一些关于前端开发和后端开发的看法。 首先,让我们将编程世界的大城市比作前端开发和后端开…...

从php页面插入MySQL的数据变为乱码如何解决?
在 PHP 页面中向 MySQL 数据库插入数据时,如果数据出现乱码,可能是因为字符集设置不正确或者字符编码不匹配。 数据库字符集设置不正确: 确保数据库的字符集设置与您的应用程序所使用的字符集一致。通常情况下,UTF-8 是一个通用的…...

OpenCV防抖实践及代码解析笔记
视频防抖是指用于减少摄像机运动对最终视频的影响的一系列方法。摄像机的运动可以是平移(比如沿着x、y、z方向上的运动)或旋转(偏航、俯仰、翻滚)。 正如你在上面的图片中看到的,在欧几里得运动模型中,图像…...

函数栈帧的创建与销毁剖析
目录 一、前言 二、基础知识介绍 2.1 寄存器介绍 2.2、汇编指令介绍 三、函数栈帧的创建销毁过程 3.1 调用main函数的函数 3.2 main函数开辟栈帧 3.3 在main函数中创建变量 3.4 调用Add函数前的准备 3.5 为Add函数开辟栈帧 3.6 在Add函数中创建变量并运算 3.7 Add函…...