您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > javascript

谈谈JS中的浅拷贝与深拷贝

时间:2023-06-27 14:43:26  来源:  作者:尚硅谷教育

在前端面试当中,经常会被问到浅拷贝与深拷贝的问题,这主要是考察面试者对基本数据类型和引用数据类型的理解,今天我们就通过本篇帮助大家详细理解浅拷贝和深拷贝的概念以及实现的几种方式。

一、认识浅拷贝和深拷贝

赋值不属于拷贝

首先,大家需要区分,赋值不属于拷贝:

let arr = [1,2,3]

let arr1 = arr

// 这里仅仅是把数组的内存地址赋值给arr1,这里不叫拷贝

概念

浅拷贝与深拷贝主要是作用于多层级数组或对象时存在的情况,多层级数组及对象举例如下:

let arr = [1,2,[3,4],{n:1}] // 多层级数组,数组里还有数组或对象

let obj = {a:1,b:2,c:{d:3,e:[1,2]}} // 多层级对象,对象里还有数组或对象

(1)浅拷贝:指只对对象或数组的第一层进行复制,其他层级复制的是所存储的内存地址。举例如下:

let arr = [2, 3, [4, 6]]

let arr1 = [...arr] // 这里我们运用扩展运算符浅拷贝了数组arr

console.log(arr === arr1)

// false,可以看到浅拷贝的数组arr1和arr指向的是不同的内存地址

arr[0] = 0 // 这里改动原数组第一个元素

console.log(arr) // [0,3,[4,6]],原数组发生了变化

console.log(arr1) // [2,3,[4,6]],新数组无变化

console.log(arr[2] === arr1[2])

// true,但是它们的第三个元素[4,6],指向的都是同一个数组

// 这里我们修改原数组的第三个元素[4,6]的第一个元素,把4改为1

arr[2][0] = 1

console.log(arr1) // [2, 3, [1, 6]],此时打印新数组,发现它也发生了改变

通过上例可以看出浅拷贝虽然复制出了一个新的数组,但是当数组的元素为引用数据类型时,浅拷贝只拷贝了地址,通过原数组改动这个地址指向的数组,新数组同样也会发生变化。

(2)深拷贝:会构造一个新的复合数组或对象,遇到引用所指向的引用数据类型会继续执行拷贝。用于解决浅拷贝只能拷贝一层的情况。举例如下:

let arr = [2, 3, [4, 6]]

let arr1 = JSON.parse( JSON.stringify(arr) )

// 通过数组转字符串再字符串转数组的方法进行了深拷贝

console.log(arr === arr1)

// false,可以看到深拷贝的数组arr1和arr指向的是不同的内存地址

console.log(arr[2] === arr1[2])

// false,即使是数组里第二层级的数组也是不相同

通过上例可以看出深拷贝是每一个层级都在堆内存中开辟了新的空间,是拷贝了一个全新的数组或对象,不会受原数组或原对象的影响。

二、实现浅拷贝的常用方法

方法1:通过扩展运算符实现

扩展运算符的方式既可以浅拷贝数组(上面已举例),也可以浅拷贝对象,这里我们再举一个浅拷贝对象的例子:

let obj = {a:1,b:2,c:{d:3,e:[1,2]}}

let obj1 = {...obj}

// 通过扩展运算符浅拷贝,获得对象obj1

console.log(obj === obj1)

// false,obj和obj1分别指向不同的对象

console.log(obj.c === obj1.c)

// true,但是obj的c属性的值和obj1的c属性的值是同一个内存地址

方法2:通过Object.assign方法实现

Object.assign()方法只适用于对象,可以实现对象的合并,语法:

Object.assign(target, source_1, ..., source_n).

Object.assign()方法会将source里面的可枚举属性复制到target,复制的是属性值,如果属性值是一个引用类型,那么复制的是引用地址,因此也属于浅拷贝。举例如下:

let target= {

name: "小明",

}

let obj1 = {

age: 28,

sex: "男",

}

let obj2 = {

friends: ['朋友1','朋友2','朋友3'],

sayHi: function (){

console.log( 'hi' )

},

}

let obj = Object.assign(target,obj1,obj2)

console.log(obj === target) // true,因此可以用变量接收结果,也可以直接使用target

obj1.age = 30 // 把obj1的age属性值改成30

console.log("target",target)

console.log("obj1",obj1)

上面打印结果如下:

我们可以看出返回的结果obj和target都指向浅拷贝的新对象,修改obj1的属性age不会影响target的age属性值。

此时给target的friends属性添加一个新的朋友4,操作如下:

target.friends.push("朋友4")

console.log("target",target)

console.log("obj2",obj2)

我们再来看看上面的打印结果:

此时target的friends属性和obj2的friends属性的值指向同一个数组。

三、实现深拷贝的常用方法

方法1:通过递归复制所有层级实现

这里我们通过封装一个deepClone函数来实现深层次拷贝,该方法适用于对象或数组,代码如下:

let obj = {

name: '小明',

age: 20,

arr: [1, 2],

}

function deepClone(value) {

// 判断传入参数不是对象或数组时直接返回传入的值,不再执行函数

if (typeof value !== 'object' || value == null) {

return value

}

//定义函数的返回值

let result

// 判断传进来的数据类型数组还是对象,对应创建新的空数组或对象

if (value instanceof Array) {

result = []

} else {

result = {}

}

// 循环遍历拷贝

for (let key in value) {

//函数递归实现深层拷贝

result[key] = deepClone(value[key])

}

// 将拷贝的结果返回出去

return result

}

let newObj = deepClone(obj)

obj.arr[0] = 0 // 修改原对象的arr属性对应的数组的元素值

console.log("obj",obj)

console.log("newObj ",newObj )

以下是上面代码的打印结果:

我们可以看到深层递归的方式不会复制引用地址,所以用原对象obj修改其arr属性对应的数组的元素,并不会影响新的对象newObj。

方法2:通过JSON对象的stringify和parse方法实现

上面我们讲解深拷贝概念时用过该方法深拷贝数组,这里我们举例来深拷贝对象:

let obj = {

name: '小明',

age: 20,

arr: [1, 2],

}

let obj1= JSON.parse( JSON.stringify(obj) )

console.log(obj.arr === obj1.arr)

// false,此时obj的arr属性和obj1的arr属性值不是同一个数组

通过代码我们可以发现,JSON.stringify()方法会把obj先转化为字符串,字符串就已经不代表任何空间地址了,就是单纯的字符串,而JSON.parse()方法把字符串解析成新对象,对象的每个层级都会在堆内存中开辟新空间。

总结

JS的浅拷贝与深拷贝主要是作用于多层级数组或对象中。浅拷贝是只复制创建数组或对象的第一层,其他层级和原数组或对象拥有相同地址值,因此修改浅拷贝的数组或对象的深层的数值就会影响原数组或对象的值。而深拷贝则是拷贝一个全新的数组或对象,每一个层级都在堆内存中开辟了新的空间,和原数组或对象相互不影响。



Tags:JS   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
又出新JS运行时了!JS运行时大盘点
Node.js是基于Google V8引擎的JavaScript运行时,以非阻塞I/O和事件驱动架构为特色,实现全栈开发。它跨平台且拥有丰富的生态系统,但也面临安全性、TypeScript支持和性能等挑战...【详细内容】
2024-03-21  Search: JS  点击:(25)  评论:(0)  加入收藏
GitHub顶流"Web OS"——运行于浏览器的桌面操作系统、用户超100万、原生jQuery和JS编写
Puter 是近日在 GitHub 上最受欢迎的一款开源项目,正式开源还没到一周 ——star 数就已接近 7k。作者表示这个项目已开发 3 年,并获得了超过 100 万用户。根据介绍,P...【详细内容】
2024-03-10  Search: JS  点击:(17)  评论:(0)  加入收藏
前端开始“锈化”?Vue团队开源JS打包工具:基于Rust、速度极快、尤雨溪主导
Vue 团队已正式开源Rolldown —— 基于 Rust 的 JavaScrip 打包工具。Rolldown 是使用 Rust 开发的 Rollup 替代品,它提供与 Rollup 兼容的应用程序接口和插件接口...【详细内容】
2024-03-09  Search: JS  点击:(11)  评论:(0)  加入收藏
如何在Rust中操作JSON,你学会了吗?
sonic-rs ​还具有一些额外的方法来进行惰性评估和提高速度。例如,如果我们想要一个 JSON​ 字符串文字,我们可以在反序列化时使用 LazyValue​ 类型将其转换为一个仍然带有斜...【详细内容】
2024-02-27  Search: JS  点击:(47)  评论:(0)  加入收藏
JS小知识,使用这6个小技巧,避免过多的使用 if 语句
最近在重构我的代码时,我注意到早期的代码使用了太多的 if 语句,达到了我以前从未见过的程度。这就是为什么我认为分享这些可以帮助我们避免使用过多 if 语句的简单技巧很重要...【详细内容】
2024-01-30  Search: JS  点击:(56)  评论:(0)  加入收藏
花 15 分钟把 Express.js 搞明白,全栈没有那么难
Express 是老牌的 Node.js 框架,以简单和轻量著称,几行代码就可以启动一个 HTTP 服务器。市面上主流的 Node.js 框架,如 Egg.js、Nest.js 等都与 Express 息息相关。Express 框...【详细内容】
2024-01-16  Search: JS  点击:(88)  评论:(0)  加入收藏
JS 中如何克隆对象?你学会了吗?
大家好,这里是大家的林语冰。JS 中如何克隆对象?此问题看似简单,实际十分复杂。假设我们需要获取下述对象的拷贝。const cat = { name: '薛定谔', girlFriends: { na...【详细内容】
2024-01-05  Search: JS  点击:(104)  评论:(0)  加入收藏
理解 Node.js 中的事件循环
你已经使用 Node.js 一段时间了,构建了一些应用程序,尝试了不同的模块,甚至对异步编程感到很舒适。但是有些事情一直在困扰着你——事件循环(Event Loop)。如果你像我...【详细内容】
2024-01-05  Search: JS  点击:(113)  评论:(0)  加入收藏
彻底搞懂 JS 类型转换
1. 什么是类型转换?Javascript 是一种弱类型语言,这意味着变量是没有明确类型的,而是由 JavaScript 引擎在编译时隐式完成。类型转换就是将一种数据类型转换为另一种数据类型,例...【详细内容】
2024-01-03  Search: JS  点击:(104)  评论:(0)  加入收藏
.NET配置文件大揭秘:轻松读取JSON、XML、INI和环境变量
概述:.NET中的IConfiguration接口提供了一种多源读取配置信息的灵活机制,包括JSON、XML、INI文件和环境变量。通过示例,清晰演示了从这些不同源中读取配置的方法,使配置获取变得...【详细内容】
2023-12-28  Search: JS  点击:(92)  评论:(0)  加入收藏
▌简易百科推荐
17 个你需要知道的 JavaScript 优化技巧
你可能一直在使用JavaScript搞开发,但很多时候你可能对它提供的最新功能并不感冒,尽管这些功能在无需编写额外代码的情况下就可以解决你的问题。作为前端开发人员,我们必须了解...【详细内容】
2024-04-03  前端新世界  微信公众号  Tags:JavaScript   点击:(5)  评论:(0)  加入收藏
你不可不知的 15 个 JavaScript 小贴士
在掌握如何编写JavaScript代码之后,那么就进阶到实践——如何真正地解决问题。我们需要更改JS代码使其更简单、更易于阅读,因为这样的程序更易于团队成员之间紧密协...【详细内容】
2024-03-21  前端新世界  微信公众号  Tags:JavaScript   点击:(27)  评论:(0)  加入收藏
又出新JS运行时了!JS运行时大盘点
Node.js是基于Google V8引擎的JavaScript运行时,以非阻塞I/O和事件驱动架构为特色,实现全栈开发。它跨平台且拥有丰富的生态系统,但也面临安全性、TypeScript支持和性能等挑战...【详细内容】
2024-03-21  前端充电宝  微信公众号  Tags:JS   点击:(25)  评论:(0)  加入收藏
构建一个通用灵活的JavaScript插件系统?看完你也会!
在软件开发中,插件系统为应用程序提供了巨大的灵活性和可扩展性。它们允许开发者在不修改核心代码的情况下扩展和定制应用程序的功能。本文将详细介绍如何构建一个灵活的Java...【详细内容】
2024-03-20  前端历险记  微信公众号  Tags:JavaScript   点击:(20)  评论:(0)  加入收藏
对JavaScript代码压缩有什么好处?
对JavaScript代码进行压缩主要带来以下好处: 减小文件大小:通过移除代码中的空白符、换行符、注释,以及缩短变量名等方式,可以显著减小JavaScript文件的大小。这有助于减少网页...【详细内容】
2024-03-13  WangLiwen    Tags:JavaScript   点击:(2)  评论:(0)  加入收藏
跨端轻量JavaScript引擎的实现与探索
一、JavaScript 1.JavaScript语言JavaScript是ECMAScript的实现,由ECMA 39(欧洲计算机制造商协会39号技术委员会)负责制定ECMAScript标准。ECMAScript发展史: 2.JavaScript...【详细内容】
2024-03-12  京东云开发者    Tags:JavaScript   点击:(2)  评论:(0)  加入收藏
面向AI工程的五大JavaScript工具
令许多人惊讶的是,一向在Web开发领域中大放异彩的JavaScript在开发使用大语言模型(LLM)的应用程序方面同样大有价值。我们在本文中将介绍面向AI工程的五大工具,并为希望将LLM...【详细内容】
2024-02-06    51CTO  Tags:JavaScript   点击:(53)  评论:(0)  加入收藏
JS小知识,使用这6个小技巧,避免过多的使用 if 语句
最近在重构我的代码时,我注意到早期的代码使用了太多的 if 语句,达到了我以前从未见过的程度。这就是为什么我认为分享这些可以帮助我们避免使用过多 if 语句的简单技巧很重要...【详细内容】
2024-01-30  前端达人  今日头条  Tags:JS   点击:(56)  评论:(0)  加入收藏
18个JavaScript技巧:编写简洁高效的代码
本文翻译自 18 JavaScript Tips : You Should Know for Clean and Efficient Code,作者:Shefali, 略有删改。在这篇文章中,我将分享18个JavaScript技巧,以及一些你应该知道的示例...【详细内容】
2024-01-30  南城大前端  微信公众号  Tags:JavaScript   点击:(67)  评论:(0)  加入收藏
使用 JavaScript 清理我的 200GB iCloud,有了一个意外发现!
本文作者在综合成本因素之下,决定用 Java 脚本来清理一下自己的 iCloud,结果却有了一个意外发现,即在 iCloud 中上传同一个视频和删除此视频之后,iCloud 的空间并不一致,这到底是...【详细内容】
2024-01-11    CSDN  Tags:JavaScript   点击:(99)  评论:(0)  加入收藏
站内最新
站内热门
站内头条