当前位置: 首页 > news >正文

【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快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【vue教程】二. Vue特性原理详解
  • 漏洞挖掘 | EDU拿敏感信息的骚思路
  • 如何构建全生命周期的安全体系架构来确保容器的安全?
  • ARM功耗管理之功耗数据与功耗收益评估
  • FastAPI 学习之路(四十九)WebSockets(五)修复接口测试中的问题
  • ScrapySharp框架:小红书视频数据采集的API集成与应用
  • 使用Docker创建并运行一个create-react-app应用(超简单)
  • 新手-前端生态
  • Qt中https的使用,报错TLS initialization failed和不能打开ssl.lib问题解决
  • Spring Boot(八十):Tesseract实现图片文字自动识别
  • Linux Zip 命令指南
  • [Spring] Spring Web MVC案例实战
  • SpringCloud集成kafka集群
  • MyBatis是如何分页的及原理
  • AWS CDN新增用户ip 地区 城市 响应头
  • 【5+】跨webview多页面 触发事件(二)
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • Javascript基础之Array数组API
  • JS 面试题总结
  • Odoo domain写法及运用
  • Promise面试题2实现异步串行执行
  • 电商搜索引擎的架构设计和性能优化
  • 海量大数据大屏分析展示一步到位:DataWorks数据服务+MaxCompute Lightning对接DataV最佳实践...
  • 机器学习中为什么要做归一化normalization
  • 技术胖1-4季视频复习— (看视频笔记)
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 我这样减少了26.5M Java内存!
  • Linux权限管理(week1_day5)--技术流ken
  • 大数据全解:定义、价值及挑战
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ###项目技术发展史
  • #systemverilog# 之 event region 和 timeslot 仿真调度(十)高层次视角看仿真调度事件的发生
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (ZT) 理解系统底层的概念是多么重要(by趋势科技邹飞)
  • (教学思路 C#之类三)方法参数类型(ref、out、parmas)
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (十六)视图变换 正交投影 透视投影
  • (图)IntelliTrace Tools 跟踪云端程序
  • (转)自己动手搭建Nginx+memcache+xdebug+php运行环境绿色版 For windows版
  • (转载)Linux网络编程入门
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .net core Redis 使用有序集合实现延迟队列
  • .Net Core缓存组件(MemoryCache)源码解析
  • .NET Micro Framework 4.2 beta 源码探析
  • .NET 反射的使用
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .net6解除文件上传限制。Multipart body length limit 16384 exceeded
  • .NET单元测试使用AutoFixture按需填充的方法总结
  • .NET企业级应用架构设计系列之应用服务器
  • .NET正则基础之——正则委托
  • /usr/bin/python: can't decompress data; zlib not available 的异常处理
  • @Pointcut 使用