摘要:之所以会出现深浅拷贝的问题,实质上是由于JS对基本类型和引用类型的处理不同。

本文分享自华为云社区《js的深浅复制,一看就明白》,作者: 鑫2020。

浅复制的意思

浅复制是仅仅对数据存放在栈内的引用的复制,没有复制引用指向堆内的内容。多个数据的浅复制,这复制多个引用,这多个引用共同指向堆内的同一个内容。当一个浅复制数据做出修改,即堆内的引用指向的内容发生修改,这时,其他通过引用指向这里的数据也会随着改变。

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = obj;
objA.a = 'a'; console.log(obj.a); // 'a'
console.log(objA.a); // 'a'

深复制的意思

深复制是指连同堆的内容一块复制,生成一个新的对象。多个深复制将是多个不同的对象,也就有不同的引用,也就指向不同的堆内容。

使用深复制的原由

在平常开发中,有时会有数据的传递与接收,当拿到传过来的数据后,难免需要对数据进行加工和改造,为了不破坏原有数据结构,这时就可以使用深复制拷贝数据,然后处理生成的新的数据。深复制也可以防止修改多个引用后引用混乱的问题,减少BUG的产生机会。

可实现深复制的几种方法

实现方式一:JSON的序列化与反序列化

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = JSON.parse(JSON.stringify(obj));//JSON的序列化与反序列化
objA.a = 'a'; console.log(obj.a); // 1
console.log(objA.a); // 'a'

虽然JSON的序列化与反序列化可以实现深复制,但有几个缺点需要注意:
1、date日期对象被转成日期日期字符串
2、没法访问到原型
3、复制不了undefined的属性
4、NAN和无穷被转为NULL

let d1 = new Date();
let obj = {
d1,
d2: undefined,
d3:NaN
}
let objD = JSON.parse(JSON.stringify(obj));
console.log(obj)
console.log(objD)

实现方式二:Object.assign()

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = Object.assign(obj);
objA.a = 'a'; console.log(obj.a); // 1
console.log(objA.a); // 'a'

虽然Object.assign()可以实现深复制,但对于更深层次的对象引用也是仅仅浅复制。

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = Object.assign(obj);
objA.c.c1 = 'c1'; //Object.assign()仅仅是一层深复制。 console.log(obj.c.c1); // 'c1'
console.log(objA.c.c1); // 'c1'

实现方式三:扩展运算符

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = {...obj};;
objA.a = 'a'; console.log(obj.a); // 1
console.log(objA.a); // 'a'

虽然扩展运算符"…"可以实现深复制,但对于更深层次的对象引用也是仅仅浅复制。

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = {...obj};
objA.c.c1 = 'c1'; //扩展运算符"..."同Object.assign()一样,仅仅是一层深复制,不能多层深复制。 console.log(obj.c.c1); // 'c1'
console.log(objA.c.c1); // 'c1'

实现方式四:使用递归

想要实现深复制,且实现多层深复制则可以使用递归循环复制。

let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} const ReCopy = function (paramter) {
let target = null;
let isObject = paramter.constructor === Object;
let isArray = paramter.constructor === Array;
if (isObject || isArray) {
target = Array.isArray(paramter) ? [] : {};
for (let i in paramter) {
target[i] = ReCopy(paramter[i]);
}
} else {
target = paramter;
}
return target;
} let objA = ReCopy(obj);
objA.c.c1 = 'c1'; console.log(obj.c.c1); // 10
console.log(objA.c.c1); // 'c1'

ladash深拷贝

lodash深复制是更专业的深复制方式。

  • 安装lodash
    先初始化,生成package.json文件,然后使用一下命令安装。
npm i -S lodash
  • 引入lodash
var _ = require('lodash');
  • 使用lodash
let obj = {
a:1,
b:2,
c:{
c1:10,
c2:20
}
} let objA = _.cloneDeep(obj);
objA.c.c1 = 'c1'; console.log(obj.c.c1); // 10
console.log(objA.c.c1); // 'c1'

点击关注,第一时间了解华为云新鲜技术~

JS的深浅复制,原来如此!的更多相关文章

  1. JavaScript的深浅复制

    JavaScript的深浅复制 为什么有深复制.浅复制? JavaScript中有两种数据类型,基本数据类型如undefined.null.boolean.number.string,另一类是Obje ...

  2. js 对象深复制,创建对象和继承

    js 对象深复制,创建对象和继承.主要参考高级编程第三版,总结网上部分资料和自己的代码测试心得.每走一小步,就做一个小结. 1.对象/数组深复制 一般的=号传递的都是对象/数组的引用,如在控制台输入 ...

  3. 详谈OC(object-c)深浅复制/拷贝-什么情况下用retain和copy

    读前小提示:对于深浅复制有一个清楚的了解,对于学习oc的朋友来说,至关重要.那么首先,我们要明白深浅复制是如何定义的呢.这里为了便于朋友们理解,定义如下. 浅 复 制:在复制操作时,对于被复制的对象的 ...

  4. OC-深浅复制

    [OC学习-26]对象的浅拷贝和深拷贝——关键在于属性是否可被拷贝 对象的拷贝分为浅拷贝和深拷贝, 浅拷贝就是只拷贝对象,但是属性不拷贝,拷贝出来的对象和原来的对象共用属性,即指向同一个属性地址. 深 ...

  5. Objective-c 深浅复制

    深浅复制的定义: 浅复制:在复制时,对于被复制对象的每一层都是指针复制. 深复制:在复制时,对于被复制的对象至少有一层是对象复制. 完全复制:在复制时,对于被复制对象的每一层都是完全复制. retai ...

  6. [置顶] js中如何复制一个对象,如何获取所有属性和属性对应的值

    在js中如何复制一个对象,例如如下一个js对象. 如果知道这个对象的所有属性自然就可以重新new一个,然后对每个属性赋值,就可以做到,但如果不知道呢?如何创建一个内容相同 的对象呢? var obj= ...

  7. Python基础之列表深浅复制和列表推导式

    一.列表深浅复制: 浅拷贝内存图如下: 深拷贝内存图如下: 二.列表推导式: 实例: """ 列表推导式 练习:exercise01 """ ...

  8. Cocos Creator JS web平台复制粘贴代码(亲测可用)

    Cocos Creator JS web平台复制粘贴代码(亲测可用) 1 webCopyString: function(str){ var input = str; const el = docum ...

  9. C# 深浅复制 MemberwiseClone(转载)

    最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考MSDN:https://docs.microsoft.com/zh-cn/dotnet/api/syst ...

  10. C# 深浅复制 MemberwiseClone

    学无止境,精益求精 十年河东,十年河西,莫欺少年穷 学历代表你的过去,能力代表你的现在,学习代表你的将来 最近拜读了大话设计模式:原型模式,该模式主要应用C# 深浅复制来实现的!关于深浅复制大家可参考 ...

随机推荐

  1. 带着问题去分析:Spring Bean 生命周期

    1: Bean在Spring容器中是如何存储和定义的 Bean在Spring中的定义是_org.springframework.beans.factory.config.BeanDefinition_ ...

  2. NOI 2023 游记

    Day0=2023.7.23. Day -?? 订了 30 个徽章.很快认识到可能不够,又自己买了 30 个. Day -? UNR,完全没有精神状态于是两天都考半场睡半场.0+10+55,成功 Fe ...

  3. RLChina2022公开课-博弈论

    纯博弈: 单纯的动机组合,离散的集合 混合博弈: 加入了概率论,以百分比的概率执行不同的的动机.,概率分布 零和博弈.合作博弈.协同博弈 扩展博弈和非完美信息 扩展博弈.贝叶斯博弈 纳什均衡 任何一位 ...

  4. Senparc 基础库全面适配 .NET 8.0

    概要 Senparc 全家桶中的基础库已经全面适配 .NET 8.0,目前随着 .NET 8.0 的 RC 版本不断发布,对应的版本号也将同步进行更新,直到本月 Ignite 大会微软官方发布 .NE ...

  5. 文心一言 VS 讯飞星火 VS chatgpt (140)-- 算法导论11.4 5题

    五.用go语言,考虑一个装载因子为a的开放寻址散列表.找出一个非零的a值,使得一次不成功查找的探查期望数是一次成功查找的探查期望数的 2 倍.这两个探查期望数可以使用定理11.6 和定理 11.8 中 ...

  6. iOS内存管理机制

    这世上,没有谁活得比谁容易,只是有人在呼天抢地,有人在默默努力.   随着科技的发展,移动设备的内存越来越大,设备的运行速度也越来越快,但是相对于整个应用市场上成千上万的应用容量来说,还是及其有限的. ...

  7. 总结--flask部分

    Flask框架的诞生: Flask诞生于2010年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架. Flask本身相当于一个内核,其他几 ...

  8. 第一次使用 GoLand 启动 运行 Go 项目

    一.开始之前确保已经安装 go语言环境 二.新建项目 三.选择项目路径 四.在该目录下手动创建 bin,src两个文件夹 bin 用来存放编译后的 .exe 文件 src 我们的工程的开发文件存放的点 ...

  9. 【总结】MySQL使用优化

    一.表设计 1.避免使用null  占用额外空间.索引无效.检索麻烦 2.能用int 不用varchaer,能用varchaer 不用text 3.int 最好给默认值 0 .varchar empt ...

  10. jvm的jshell,学生的工具

    jshell  在我眼里,只能作为学校教学的一个玩具,事实上官方也做了解释,以下是官方的解释: 在学习编程语言时,即时反馈很重要,并且 它的 API.学校引用远离Java的首要原因 教学语言是其他语言 ...