深拷贝浅拷贝的区别?如何实现一个深拷贝?
一、数据类型存储
JavaScript中存在两大数据类型:
- 基本类型 Number String null Undefined Boolean symbol
- 引用类型 array object function
基本类型数据保存在在栈内存中
引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的引用,存在栈中
二、浅拷贝
浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝
如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址
即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址
下面简单实现一个浅拷贝
function shallowClone(obj) {const newObj = {};for(let prop in obj) {if(obj.hasOwnProperty(prop)){newObj[prop] = obj[prop];}}return newObj;
}
在JavaScript中,存在浅拷贝的现象有:
- Object.assign
- Array.prototype.slice(), Array.prototype.concat()
- 使用拓展运算符实现的复制
Object.assign
var obj = {age: 18,nature: ['smart', 'good'],names: {name1: 'fx',name2: 'xka'},love: function () {console.log('fx is a great girl')}
}
var newObj = Object.assign({}, fxObj);
slice()
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.slice(0)
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
concat()
const fxArr = ["One", "Two", "Three"]
const fxArrs = fxArr.concat()
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
拓展运算符
const fxArr = ["One", "Two", "Three"]
const fxArrs = [...fxArr]
fxArrs[1] = "love";
console.log(fxArr) // ["One", "Two", "Three"]
console.log(fxArrs) // ["One", "love", "Three"]
三、深拷贝
深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性
常见的深拷贝方式有:
- _.cloneDeep()
- jQuery.extend()
- JSON.stringify()
- 手写循环递归
_.cloneDeep()
const _ = require('lodash');
const obj1 = {a: 1,b: { f: { g: 1 } },c: [1, 2, 3]
};
const obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);// false
jQuery.extend()
const $ = require('jquery');
const obj1 = {a: 1,b: { f: { g: 1 } },c: [1, 2, 3]
};
const obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f); // false
JSON.stringify()
const obj2=JSON.parse(JSON.stringify(obj1));
但是这种方式存在弊端,会忽略undefined、symbol和函数
const obj = {name: 'A',name1: undefined,name3: function() {},name4: Symbol('A')
}
const obj2 = JSON.parse(JSON.stringify(obj));
console.log(obj2); // {name: "A"}
循环递归
function deepClone(obj, hash = new WeakMap()) {if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作if (obj instanceof Date) return new Date(obj);if (obj instanceof RegExp) return new RegExp(obj);// 可能是对象或者普通的值 如果是函数的话是不需要深拷贝if (typeof obj !== "object") return obj;// 是对象的话就要进行深拷贝if (hash.get(obj)) return hash.get(obj);let cloneObj = new obj.constructor();// 找到的是所属类原型上的constructor,而原型上的 constructor指向的是当前类本身hash.set(obj, cloneObj);for (let key in obj) {if (obj.hasOwnProperty(key)) {// 实现一个递归拷贝cloneObj[key] = deepClone(obj[key], hash);}}return cloneObj;
}
四、区别
下面首先借助两张图,可以更加清晰看到浅拷贝与深拷贝的区别

从上图发现,浅拷贝和深拷贝都创建出一个新的对象,但在复制对象属性的时候,行为就不一样
浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象
// 浅拷贝
const obj1 = {name : 'init',arr : [1,[2,3],4],
};
const obj3=shallowClone(obj1) // 一个浅拷贝方法
obj3.name = "update";
obj3.arr[1] = [5,6,7] ; // 新旧对象还是共享同一块内存console.log('obj1',obj1) // obj1 { name: 'init', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3',obj3) // obj3 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象
// 深拷贝
const obj1 = {name : 'init',arr : [1,[2,3],4],
};
const obj4=deepClone(obj1) // 一个深拷贝方法
obj4.name = "update";
obj4.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存console.log('obj1',obj1) // obj1 { name: 'init', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4',obj4) // obj4 { name: 'update', arr: [ 1, [ 5, 6, 7 ], 4 ] }
小结
前提为拷贝类型为引用类型的情况下:
- 浅拷贝是拷贝一层,属性为对象时,浅拷贝是复制,两个对象指向同一个地址
- 深拷贝是递归拷贝深层次,属性为对象时,深拷贝是新开栈,两个对象指向不同的地址
总结:
- 深拷贝递归地复制新对象中的所有值或属性,而拷贝只复制引用。
- 在深拷贝中,新对象中的更改不会影响原始对象,而在浅拷贝中,新对象中的更改,原始对象中也会跟着改。
- 在深拷贝中,原始对象不与新对象共享相同的属性,而在浅拷贝中,它们具有相同的属性。
相关文章:
深拷贝浅拷贝的区别?如何实现一个深拷贝?
一、数据类型存储 JavaScript中存在两大数据类型: 基本类型 Number String null Undefined Boolean symbol引用类型 array object function 基本类型数据保存在在栈内存中 引用类型数据保存在堆内存中,引用数据类型的变量是一个指向堆内存中实际对象的…...
Linux应用编程下连接本地数据库进行增删改查系列操作
文章目录前言一、常用SQL操作语句二、相关函数解析三、连接本地数据库四、编译运行五、程序源码前言 本篇为C语言应用编程下连接Linux本地数据库进行增删改查系列操作。 在此之前,首先当然是你需要具备一定的数据库基础,所以下面我先列出部分常用的SQL…...
图论学习03
图神经网络模型介绍 将图神经网络分为基于谱域上的模型和基于空域上的模型,并按照发展顺序详解每个类别中的重要模型。 基于谱域的图神经网络 谱域上的图卷积在图学习迈向深度学习的发展历程上起到了关键性的作用。三个具有代表性的谱域图神经网络 谱图卷积网络切…...
解决qt中cmake单独存放 .ui, .cpp, .h文件
设想 项目文件较多,全部放在一个目录下就像依托答辩。 希望能将头文件放入include,ui文件放入ui,源文件放入src。 为了将Qt代码和一般非Qt代码分离开,进一步地: 将Qt源文件放入qt_src,普通源文件放入sr…...
操作系统(day12)-- 基本分段存储,段页式存储
基本分段存储管理方式 不会产生内部碎片,会产生外部碎片 分段 按照程序自身的逻辑关系划分为 若干个段,每个段都有一个段名,每段从0开始编址 分段存储管理方式中一个段表项由段号(隐含)、段长、基地址 分段的段表项固…...
疯狂弹出请插入多卷集的最后一张磁盘窗口
整个人嘛了,今天插上U盘,跟tmd中了病毒一样, 屏幕疯狂弹出窗口, 提示请插入多卷集的最后一张磁盘! 点确定之后他继续弹出,点取消它也继续弹出, 关掉一个又弹出来一个,妈的&#x…...
Spark12: SparkSQL入门
一、SparkSQL Spark SQL和我们之前讲Hive的时候说的hive on spark是不一样的。hive on spark是表示把底层的mapreduce引擎替换为spark引擎。而Spark SQL是Spark自己实现的一套SQL处理引擎。Spark SQL是Spark中的一个模块,主要用于进行结构化数据的处理。它提供的最核…...
show profile和trance分析SQL
目录 一.show profile分析SQL 二.trance分析优化器执行计划 一.show profile分析SQL Mysql从5.0.37版本开始增加了对show profiles和show profile语句的支持。show profiles能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。。 通过have_profiling参数,能够…...
[AI生成图片] 效果最好的Midjourney 的介绍和使用
Midjourney介绍: 是一个文本生成图片的扩散模型,能够根据输入的任何文本生成令人难以置信的图像,让数十亿人在几秒钟内创造惊人的艺术。为方便用户控制和快速生成图片,打开后在页面底部输入文本内容,稍等一小会&#…...
Vue.use( ) 的核心原理
首先来思考几个问题: Vue.use是什么? vue.use() 是vue提供的一个静态方法,主要是为了注册插件,增加vue的功能。 Vue.use( plugin ) plugin只能是Object 或 Function vue.use()做了什么工作? 该js如果是对象 该对象…...
idea同时编辑多行-winmac都支持
1背景介绍 idea编辑器非常强大,其中一个功能非常优秀,很多程序员也非常喜欢用。这个功能能够大大大提高工作效率-------------多行代码同时编辑 2win 2.1方法1 按住alt鼠标左键上/下拖动即可 这样选中多行后,可以直接多行编辑。 优点&a…...
亿级高并发电商项目-- 实战篇 --万达商城项目 十一(编写商品搜索功能、操作商品同步到ES、安装RabbitMQ与Erlang,配置监听队列与消息队列)
👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者 📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶 Ǵ…...
数据结构概述和稀疏数组
数据结构和算法内容介绍 1)算法是程序的灵魂,优秀的程序可以在海量数据计算时,仍然保持高速计算 数据结构和算法概述 1)程序 数据结构算法 2)学好数据结构可以编写出更加漂亮,更加有效率的代码 3&…...
宝塔搭建实战人才求职管理系统adminm前端vue源码(三)
大家好啊,我是测评君,欢迎来到web测评。 上一期给大家分享骑士cms后台admin前端vue在本地运行打包、宝塔发布部署的方式,本期给大家分享,后台adminm移动端后台vue前端怎么在本地运行,打包,实现线上功能更新…...
服务器是干什么用的?
首先,什么是服务器?服务器是提供计算服务器和网络服务的设备。服务器和计算机由CPU、硬盘、内存、系统总线等组成。比如我们访问一个网站,点击这个网站会发出访问请求,服务器会响应服务请求,进行相应的处理,…...
C++ 之结构体与共用体
文章目录参考描述结构体使用(基本)声明初始化先创建后初始化C 11 列表初始化使用(进阶)结构数组声明(拓展)声明及创建声明及初始化匿名结构体细节外部声明与内部声明成员赋值共用体存储空间匿名共用体同化尾…...
Java基础知识汇总(良心总结)
1. 前言 本文章是对Java基础知识进行了汇总,方便大家学习。 申明:文章的内容均来自黑马程序员,博主只是将其搬到了CSDN上以共享给大家学习 2. 目录 Day01 Java学习笔记之《开章》 Day02 Java学习笔记之《运算符》 Day03 Java学习笔记之《流程…...
InnoDB之Undo log格式
1. 前言 InnoDB有两大日志模块,分别是redo log和undo log。为了避免磁盘随机写,InnoDB设计了redo log,数据写入时只写缓冲页和redo log,脏页由后台线程异步刷盘,哪怕系统崩溃也能根据redo log恢复数据。但是我们漏了一…...
一问学习StreamAPI终端操作
Java Stream管道流是用于简化集合类元素处理的java API。 在使用的过程中分为三个阶段: 将集合、数组、或行文本文件转换为java Stream管道流管道流式数据处理操作,处理管道中的每一个元素。上一个管道中的输出元素作为下一个管道的输入元素。管道流结果…...
在屎山代码中快速找到想要的代码法-锁表法(C#)
由于本人水平有限,文中有写得不对的地方请指正,本文的方法有些投机取巧,实在是没招的情况下可以酌情使用,如有侵权,请联系删除。 前几天接到一个需求,要在医嘱签署时对检验项目进行分方操作,分…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南
文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求,并检查收到的响应。它以以下模式之一…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
