【JavaScript】聊一聊js中的浅拷贝与深拷贝与手写实现

前言

什么是深拷贝与浅拷贝?深拷贝与浅拷贝是js中处理对象或数据复制操作的两种方式。‌在聊深浅拷贝之前咱得了解一下js中的两种数据类型:

基本数据类型(6种)String、Number、Object、Boolean、null、undefined、symbol(ES6+)

引用数据类型Object(function、Array、正则表达式等皆是对象)

  • 数据的存储方式是什么?

基本数据: 基本数据类型是存放在栈中的简单数据段,它们是直接按值存放的,所以可以直接按值访问引用类型: 引用类型是存放在堆内存中的对象,保存的在栈内存中的一个指针,保存的是栈内存中对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。

1.浅拷贝

1.1 什么是浅拷贝

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址。

  • 下面用一张图来解释一下浅拷贝

image

1.2 浅拷贝实现方法

1.2.1 assign

var obj = {
	age: 18,
	person: {
		name1: 'fx',
		name2: 'xka'
	},
	list:['hhh','666'],
	love: function () {
		console.log('嘿嘿')
	}
}
var newObj = Object.assign({}, obj);
//因为是浅拷贝,所以只拷贝了基本类型的,引用类型还是共享内存地址的,即改变obj的应用类型的内容,newObj里面的引用类型的值也随之改变
obj.person.name1='xxx'
obj.list[0]='xxx'
console.log(newObj.person.name1) //xxx

1.2.2 slice

const fxArr = ["One", {
	name: "Two",
	age: 20
}, "Three"]
const fxArrs = fxArr.slice(0,)
fxArr[1].name = "four";
console.log(fxArrs[1].name) //four

1.2.3 concat

const fxArr = ["One", {
	name: "Two",
	age: 20
}, "Three"]
const fxArrs = fxArr.concat()
fxArr[1].name = "four";
console.log(fxArrs[1].name) //four

1.2.4 拓展运算符

const fxArr = ["One", {
	name: "Two",
	age: 20
}, "Three"]
const fxArrs = [...fxArr]
fxArr[1].name = "four";
console.log(fxArrs[1].name) //four

2.深拷贝

2.1 什么是深拷贝

深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性

  • 下面用一张图来解释一下深拷贝

image

2.2 浅拷贝实现方法

2.2.1 JSON.parse(常用)

var obj = {
	age: 18,
	person: {
		name1: 'fx',
		name2: 'xka'
	},
	list:['hhh','666'],
	love: function () {
		console.log('嘿嘿')
	}
}
 
const obj2=JSON.parse(JSON.stringify(obj));
obj.person.name1='6666'
console.log(obj2.person.name1) //fx

  • 我常用的基本就是JSON.parse了,然而其他的,之前听过的lodash的cloneDeep,jq的extend我都没使用过。

  • 但是适用JSON.parse会有一个缺点,就是处理的数据里面有undefined、function、symbol会被忽略,但是这也是一个优点,可以利用其特性将undefined等数据排除,拿到干净的数据。还有一个缺点就是在处理的数据比较大的话,还有性能问题。

3.手写实现深浅拷贝

3.1 浅拷贝

function clone(object){
	const newObj={}
	for(let proto in object){
		if(object.hasOwnProperty(proto)){
			newObj[proto]= object[proto]
		}
	}
	return newObj
}

var obj = {
	age: 18,
	person: {
		name1: 'fx',
		name2: 'xka'
	},
	list:['hhh','666'],
	love: function () {
		console.log('嘿嘿')
	}
}
 
const obj1=clone(obj)
console.log(obj)
console.log(obj1)

3.2 深拷贝

// 手写深拷贝
function deepClone(obj, hash = new WeakMap()) {
	// 数据过滤
	if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作 
	if (obj instanceof Date) return new Date(obj);// 如果传入的对象是日期对象,使用 new Date() 创建一个新的日期对象并返回
	if (obj instanceof RegExp) return new RegExp(obj);// 如果传入的对象是正则表达式对象,使用 new RegExp() 创建一个新的正则表达式对象并返回
	// 如果传入的对象不是普通对象(即不是对象类型)或普通的值 如果是函数的话就不需要深拷贝
	// 因为拷贝的只需要考虑是否为对象,所以只需要判断obj是否为对象类型即可,因为null或者undefined在上面已经先过滤掉了,此时就只剩下基本数据类型和函数了
	if (typeof obj !== "object") return obj;
	// 来到此处的话就只剩下对象了,就要进行深拷贝
	if (hash.get(obj)) return hash.get(obj);
	// 深拷贝
	// 创建一个新对象,这个新对象和obj对象类型相同
	// 找到的是所属类原型上的constructor属性,而原型上的constructor指向的是当前类本身
	let cloneObj = new obj.constructor();
	hash.set(obj, cloneObj);
	for (let key in obj) {
		if (obj.hasOwnProperty(key)) {
			// 实现一个递归拷贝
			cloneObj[key] = deepClone(obj[key], hash);
		}
	}
	return cloneObj;
}

var obj = {
	age: 18,
	person: {
		name1: 'fx',
		name2: 'xka'
	},
	list:['hhh','666'],
	love: function () {
		console.log('嘿嘿')
	}
}
 
 
const obj2 = deepClone(obj) // 深拷贝
const obj3 = Object.assign({}, obj) // 浅拷贝
const obj4 = clone(obj) // 浅拷贝
obj.person.name1 = 'hhh';
//因为是深拷贝,obj2中的引用类型新开辟了一个内存地址,所以obj的person改变obj2不受影响
console.log(obj2.person.name1) //fx
//因为是浅拷贝,obj3、obj4中的引用类型与obj中的引用类型共享内存地址,所以obj的person改变obj3、obj4皆受影响
console.log(obj3.person.name1) //hhh
console.log(obj4.person.name1) //hhh

文章转载自:我恨bug

原文链接:https://www.cnblogs.com/nothavebug/p/18300473

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构——线性表(C语言实现)

写在前面: 在前面C语言的结构体学习中,我提及了链表的操作, 学习数据结构我认为还是需要对C语言的数组、函数、指针、结构体有一定的了解,不然对于结构体的代码可能很难理解,特别是一些书籍上面用的还是伪代码&#xf…

Day07-员工管理-上传下载

1.员工管理-导出excel 导出员工接口返回的是二进制axios配置responseType为blob接收二进制流文件为Blob格式按装file-saver包,实现下载Blob文件npm install add file-saver导出员工excel的接口 (src/api/employee.js) export function exportEmployee(){return req…

普通人还有必要学习 Python 之类的编程语言吗?

在开始前分享一些编程的资料需要的同学评论888即可拿走 是我根据网友给的问题精心整理的对于编程的重要性,这里就不详谈了。 未来,我们和机器的交流会越来越多,编程可以简单看作是和机器对话并分发给机器任务。机器不仅越来越强大&#xff0…

芯课堂 | Synwit_UI_Creator(ugui)平台之PC端界面设计篇

​今天小编给大家介绍的是华芯微特面向小尺寸TFT-LCD屏驱市场量身打造的Synwit_UI_Creator(ugui)自研开发套件。 UI_Creator(ugui)开发套件分为上位机和下位机,以下如无特指,上位机即为PC端设计器/仿真器&…

【香橙派AiPro】基于VGG16的火灾检测模型预测

目录 引言开发板介绍开发板使用准备工作工具文档 拨码开关镜像烧录连接开发板下载MobaXterm网线-SSH连接开发板设置WIFI连接WIFI-SSH连接开发板确定开发板IP方法 Vnc可视化WindowsiPad 开发工具安装 散热风扇基于VGG16的火灾检测模型预测数据集准备目录结构代码操作 安装宝塔最…

RISC-V在线反汇编工具

RISC-V在线反汇编工具: https://luplab.gitlab.io/rvcodecjs/#q34179073&abifalse&isaAUTO 不过,似乎,只支持RV32I、RV64I、RV128I指令集:

2024大模型十大趋势

2024大模型十大趋势 关键要点一、机器外脑时代的智慧探索二、机器外脑、创意生成和情感陪伴三、大模型驱动的新未来:AI带来创意转化与机遇四、人物-行为-场景一体化:未来人工智能的新范式五、未来数字内容生产的基础设施六、共创、共建、共享智能美好未来…

【入门篇】2.3 STM32启动模式(一)

一,Boot引脚分步 二,启动电路 三,启动模式 STM32F4 根据 BOOT 引脚的电平选择启动模式,这两个 BOOT 引脚根据外部施加的电平来决定芯片的启动地址。 下表中 BOOT0 和 BOOT1 是 STM32 芯片上面的两个引脚,用于控制 STM32

哪个牌子充电宝好用?实测倍思、西圣、安克充电宝,哪个值得入手

目前充电宝已经成为我们日常出行的重要依靠。然而,共享充电宝不仅价格昂贵,而且还存在诸多安全隐患,让我们用起来总是不太放心。为了帮大家找到既好用又实惠且安全的充电宝,我们对倍思、西圣、安克这三个热门品牌的充电宝进行了深…

Ubuntu/Linux 安装ITKSnap

文章目录 1. 安装ITKSnap1.1 下载后安装 2.进入opt文件夹改名3. 更改启动项4. 创建硬链接5. 添加桌面启动方式6. 即可使用 1. 安装ITKSnap http://www.itksnap.org/pmwiki/pmwiki.php?nMain.HomePage 1.1 下载后安装 找到下载的文件夹,文件夹内打开terminal。复…

提升代码质量:利用策略模式优化Spring Boot应用的设计

📣前言 在Spring Boot中使用策略模式(Strategy Pattern)是一种常见的设计模式实践,它允许在运行时选择算法的行为。策略模式定义了一系列的算法,并将每个算法封装起来,使它们可以互换。这样做的好处是使算法…

MyBatis源码中的设计模式1

1. 建造者模式的应用 建造者模式属于创建类模式,通过一步一步地创建一个复杂的对象,能够将部件与其组装过程分开。用户只需指定复杂对象的类型,就可以得到该对象,而不需要了解其内部的具体构造细节。《Effective Java》中也提到&…

CH552G使用IAP下载

常见下载中的方式ISP,IAP,ICP 参考,CH552G中文手册,参考1 ISP:In System Programing,在系统编程。是常见的,使用软件,先将某个引脚(例如boot)连接到合适的电…

领航Linux UDP:构建高效网络新纪元

欢迎来到 破晓的历程的 博客 ⛺️不负时光,不负己✈️ 文章目录 引言Udp和Tcp的异同相同点不同点总结 1.1、socket1.2、bind1.3、recvfrom1.4、sendto2.1、代码2.1、说明3.1、代码3.2、说明 引言 在前几篇博客中,我们学习了Linux网络编程中的一些概念。…

【Django】网上蛋糕项目商城-购物车和我的订单功能

1.购物车功能 在首页中的滚动栏的商品,热门商品,新品,以及商品详情中都有加入购物车按钮 在models文件中创建购物车表,用于保存当前用户添加的商品信息 # 购物车表 class ShoppingCar(models.Model):# 用户iduserIdmodels.Integ…

JRT打印设计器解耦

为了让打印设计器可以给多个产品打印通用,那么设计器就不能嵌入太多具体产品的业务信息。比如医院主键、工作组、医嘱关联登。 设计器在设计表的时候就没引入检验部分的依赖,采用产品组唯一标识和产品组业务ID来隔离不同组的模板设计。 维护菜单时候就…

CTFshow--web--xss

目录 web316 web317~319 web320~326 web327 web328 web329 web330 web331 web332 web333 先在自己的服务器写上代码 <?php$content $_GET[1]; if(isset($content)){file_put_contents(flag.txt,$content); }else{echo no data input; }要拿到管理员的cookie , 而…

Java - 程序员面试笔记记录 实现 - Part5

7.1 Struts 优点&#xff1a; 1. MVC模式实现了表现与逻辑的分离&#xff0c;扩展性高。 2. 提供页面导航功能&#xff0c;通过配置文件建立整个系统各部分之间的联系。 3. 集成了一些常用处理功能。 缺点&#xff1a; 1. 仅面向 Web 应用程序开发 2. Action 非线程安全…

HTML+CSS+JS井字棋(来自动下棋)

井字棋 自动下棋 玩家先下&#xff0c;计算机后下 源码在图片后面 点赞❤️收藏⭐️关注&#x1f60d; 效果图 源代码 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <title>Tic Tac Toe Game</tit…

开始Linux之路

人生得一知己足矣&#xff0c;斯世当以同怀视之。——鲁迅 Linux操作系统简单操作指令 1、ls指令2、pwd命令3、cd指令4、mkdir指令(重要)5、whoami命令6、创建一个普通用户7、重新认识指令8、which指令9、alias命令10、touch指令11、rmdir指令 及 rm指令(重要)12、man指令(重要…