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

news/2024/9/19 10:52:44/文章来源:https://www.cnblogs.com/nothavebug/p/18300473

前言

什么是深拷贝与浅拷贝?深拷贝与浅拷贝是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我都没使用过。

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

上述为个人学习整理内容,水平有限,如有错误之处,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!谢谢~๑•́₃•̀๑ [鲜花][鲜花][鲜花]

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

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

相关文章

分页查询及其拓展应用案例

分页查询 分页查询是处理大量数据时常用的技术,通过分页可以将数据分成多个小部分,方便用户逐页查看。SQLAlchemy 提供了简单易用的方法来实现分页查询。 本篇我们也会在最终实现这样的分页效果:1. 什么是分页查询 分页查询是将查询结果按照一定数量分成多页展示,每页显示固…

delphi dev cxgrid 列绑定Richedti 支持过滤

默认是不支持过滤的,这里需要改到内部的一些源码文件。 先说思路: 1.要让列支持过滤需要重载richedit类的 GetSupportedOperations,typeTcxRichEditProperties = class(cxRichEdit.TcxRichEditProperties)publicfunction GetSupportedOperations: TcxEditSupportedOperation…

《项目管理》-笔记1

PMBOK解读 1.1项目和项目管理 项目:项目是为创造独特的产品、服务或成果而进行的临时性工作。 项目管理:在项目的活动中运用知识、技术、工具、技巧,以满足项目要求。 1.2十大知识领域 (1)项目整合管理 项目整合管理包括为识别、定义、组合、统一和协调各项目管理过程组的各…

Cilium Ingress 特性(转载)

Cilium Ingress 特性Cilium Ingress 特性(转载) 一、环境信息主机 IPubuntu 10.0.0.234软件 版本docker 26.1.4helm v3.15.0-rc.2kind 0.18.0kubernetes 1.23.4ubuntu os Ubuntu 22.04.6 LTSkernel 5.15.0-106二、Cilium Ingress 流程图Cilium 现在提供了开箱即用的 Kuberne…

N1盒子挂载阿里云盘-Alist工具

Markdown Example.centered-text { text-align: center; font-size: 40px; font-family: "Times New Roman", Georgia, serif }N1盒子挂载阿里云盘安装Alist手动安装 参考:官方文档step 1step 2配置-启动step 3打开web网页:http://192.168.1.254:5244/ 登录界面、拉…

WindowsLinux搭建frp内网穿透(自用)

Linux服务器搭建服务端 1、下载官方frp包,软件是开源的,下载链接: https://github.com/fatedier/frp/releases 根据自己的版本需求,自行下载对应的版本号,本文章以0.37版本为例wget -c https://github.com/fatedier/frp/releases/download/v0.37.1/frp_0.37.1_linux_amd64…

2024-07-13:用go语言,给定一个从0开始的长度为n的整数数组nums和一个从0开始的长度为m的整数数组pattern,其中pattern数组仅包含整数-1、0和1。 一个子数组nums[i.

2024-07-13:用go语言,给定一个从0开始的长度为n的整数数组nums和一个从0开始的长度为m的整数数组pattern,其中pattern数组仅包含整数-1、0和1。 一个子数组nums[i..j]的大小为m+1,如果满足以下条件,则我们称该子数组与模式数组pattern匹配: 1.若pattern[k]为1,则nums[i+…

dbeaver

修改字体 参考 【DBeaver】常用自定义设置 旧版 编辑器字体查询结果字体新版 应用字体编辑器字体修改背景色 编辑器背景色注意: 此设置会同时修改查询结果背景色,但是需要重启 dbeaver格式化配置 参考 【DBeaver】常用自定义设置 格式化大小写关闭自动插入表别名关于语句分隔符…

动态规划的“三步走”方法

“三步走”方法 动态规划问题种类较多,但大多都能通过“三步走”方法解决。状态表示:将具体问题抽象为数学问题,明确需要表示的状态,数组中的下标分别表示哪种状态。 状态转移:状态转移相当于递推公式。主要有两种方式,可以从上一个状态转移到当前状态,或者从当前状态转…

N1盒子挂载磁盘-解决盒子重启后无法自动挂载问题

Markdown Example.centered-text { text-align: center; font-size: 40px; font-family: "Times New Roman", Georgia, serif }N1盒子挂载磁盘挂载步骤: step 1step 2如果提示挂载已存在、就先卸载挂载分区step 3 回到首页重新挂载step 4此时已经挂载成功、但是默认…

记一次 CDN 流量被盗刷经历

先说损失,被刷了 70 多RMB,600多G流量,PCDN 真可恶啊,这里分享一下过程和止损手段。先说损失,被刷了 70 多RMB,还好止损相对即时了,亏得不算多,PCDN 真可恶啊。600多G流量,100多万次请求。 怎么发现的 先是看到鱼皮大佬发了一篇推文突发,众多网站流量被盗刷!我特么也…

爬虫初识

一 介绍 二 安装 三 基本使用 四 选择器 五 等待元素被加载 六 元素交互操作 七 其他 八 项目练习_____egon新书python全套来袭:https://egonlin.com/book.html一 介绍selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题…