什么是浅拷贝和深拷贝

news2024/11/24 0:30:42

javascript 中有不同的方法来复制对象,那么我们怎样才能正确地复制一个对象呢?,本文来介绍一下浅拷贝和深拷贝。

一、什么是浅拷贝(Shallow Copy)

浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

二、实现浅拷贝方式有哪些?

一、直接赋值

let obj1 = { name: '王新焱' }

let obj2 = obj1

console.log(obj2) // 输出 { name: '王新焱' }

提示:任何操作都会影响原数组;


二、Object.assign()

let obj1 = { name: '王新焱' }

let obj2 = Object.assign({}, obj1)

console.log(obj2) // 输出 { name: '王新焱' }

提示:拷贝属性值,假如属性值是一个对象的引用,那么也会指向那个引用;


三、Array.prototype.concat()

let arr1= ['王新焱']

let arr2 = ['张三丰']

let arr3 = arr1.concat(...arr2)

console.log(arr3) // 输出 ['王新焱', '张三丰']


四、Array.prototype.slice()

let n = [1, 2, 3, 4, 5]

let n1 = n.slice(0)

n[0] = '张三丰'

console.log(n) // 输出 ['张三丰', 2, 3, 4, 5]

console.log(n1) // 输出 [1, 2, 3, 4, 5]


五、扩展运算符(...)

let arr1 = ['张三丰']

let arr2 = [...arr1]

console.log(arr2) // 输出 ['张三丰']


六、实现一个浅拷贝函数
function shallowClone(obj) {
    const newObj = {};
    for(let prop in obj) {
        if(obj.hasOwnProperty(prop)){
            newObj[prop] = obj[prop];
        }
    }
    return newObj;
}

const obj1 = {
    name : '张无忌',
    arr : [1,[2,3],4],
}

const obj3 = shallowClone(obj1) // 一个浅拷贝方法
obj3.name = "张三丰";
obj3.arr[1] = [5,6,7] ; // 新旧对象还是共享同一块内存

console.log('obj1',obj1) // obj1 { name: '张无忌',  arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3',obj3) // obj3 { name: '张三丰', arr: [ 1, [ 5, 6, 7 ], 4 ] }

栈:自动分配内存空间,系统自动释放,里面存放的是基本类型的值和引用类型的地址

堆:动态分配的内存,大小不定,也不会自动释放。里面存放引用类型的值。

三、什么是深拷贝(Deep Copy)

深拷贝是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。

四、深拷贝的方法有哪些?

一、JSON.parse(JSON.stringify()) 实现深拷贝
let arr = [
    { name: '张三' },
	{ name: '李四' },
	{ name: '王五' }
]

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

copyArr[0].name = '张三丰'

console.log(arr) // [{name: '张三'}, {name: '李四'}, {name: '王五'}]

console.log(copyArr) // [{name: '张三丰'}, {name: '李四'}, {name: '王五'}]


二、循环递归实现深拷贝
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 : '张无忌',
    arr : [1,[2,3],4],
};
const obj4=deepClone(obj1) // 一个深拷贝方法
obj4.name = "张三丰";
obj4.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存

console.log('obj1',obj1) // obj1 { name: '张无忌', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4',obj4) // obj4 { name: '张三丰', arr: [ 1, [ 5, 6, 7 ], 4 ] }


三、lodash 和 jquery 第三方库实现深拷贝,它们都提供了深拷贝方法

lodash提供了cloneDeep()方法,cloneDeep() 方法通过递归复制每个属性和子对象,实现了深度复制。方法返回原始对象的完全独立副本,不共享任何引用。

const _ = require('lodash')

const obj1 = { a: 1, b: { c: 2 } }

const deepObj = _.cloneDeep(obj1)


jquery提供了extend()方法,extend() 方法可以实现深拷贝,并将一个或多个对象的内容合并到目标对象中。它能够处理对象、数组等多种类型的数据。

const $ = require('jquery');

const obj1 = { a: 1, b: { c: 2 } }

const deepObj = $.extend(true, {}, obj1)

五、为什么要使用深拷贝?

我们希望在改变新的数组(对象)的时候,不改变原数组(对象)

因为深拷贝创建了一个新对象或数据结构,该对象与原始对象完全独立,不共享内部引用,深拷贝是对原始对象的完全复制,包括所有嵌套对象和子对象,确保它们在内存中是独立的。

六、浅拷贝和深拷贝对比

特征深拷贝浅拷贝
对象间关系完全独立,不共享引用共享引用,可能有副作用
内部对象拷贝递归复制内部对象及其嵌套对象复制内部对象的引用,共享内部对象
对原始对象影响不会改变原始对象的值或状态可能会改变原始对象的值或状态
性能拷贝所有属性和嵌套对象,性能开销较大只拷贝最外层对象,性能开销相对较小
使用场景当需要完全独立的对象,避免副作用时使用当只需创建一个浅层副本,共享内部对象时使用

七、示例图 

从上图发现,浅拷贝和深拷贝都创建出一个新的对象,但在复制对象属性的时候,行为就不一样

浅拷贝只复制属性指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存,修改对象属性会影响原对象。

八、总结

选择使用深拷贝还是浅拷贝取决于具体的需求和场景。如果需要创建独立的对象、避免副作用,并确保修改副本不会影响原始对象,则应使用深拷贝。如果只需创建一个浅层副本、共享内部对象或对原始对象进行轻微修改,则可以使用浅拷贝。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

遗传算法在数学建模中的应用及MATLAB实现

2023年9月数学建模国赛期间提供ABCDE题思路加Matlab代码,专栏链接(赛前一个月恢复源码199,欢迎大家订阅):http://t.csdn.cn/Um9Zd 目录 遗传算法基本概念 遗传算法原理 MATLAB实现 1. 使用ga求解遗传算法问题 数学建模案例:旅行商问题(TSP&#xf…

操作系统原理 —— 内存连续分配管理方式(二十)

在之前的章节中,我们到了内存管理,其中有一个很重要的功能,就是对操作系统中的内存进行分配和回收。 那如何对操作系统的内存进行分配呢? 整体上可以分为两种方式:连续分配管理方式、非连续分配管理方式。 这里提到的…

【vue3】08-vue的组件化开发-插槽(Slots)的完全指南

Vue插槽(Slots)的完全指南 插槽的作用插槽的基本使用具名插槽作用域插槽(难点) 插槽的作用 在开发中,我们会经常封装一个个可复用的组件: 前面我们会通过props传递给组件一些数据,让组件来进行展示;但是为…

【CVPR2023】TPS详解:联合令牌剪枝与压缩以实现视觉变形器更积极的压缩

【CVPR2023】TPS详解:联合令牌剪枝与压缩以实现视觉变形器更积极的压缩 0. 引言1. 为什么要使用TPS?2. TPS介绍3. TPS 详解3.1 重要性计算3.2 令牌压缩3.2.1 匹配3.2.2 融合 4. 简化版理解5. 总结 0. 引言 虽然 Vision Transformers (ViTs&a…

小文智能宣布接入ChatGPT,智能化客户服务,开创全新用户体验

小文智能是一家致力于用AI技术解放劳动力的公司,最近我们接入了ChatGPT技术,深度探索AI在智能对话机器人领域应用的更多可能,这将为我们的客户带来更为优质的人机对话服务和全新的用户体验。 ChatGPT是一种基于人工智能的自然语言处理技术&a…

案例31:基于Springboot企业员工薪酬关系系统开题报告设计

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

逍遥自在学C语言 | break-循环的中断与跳转

前言 在C语言中,break语句是一种控制流语句,它用于终止当前所在的循环结构(for、while、do-while)或者switch语句,从而跳出循环或者结束switch语句的执行。 一、人物简介 第一位闪亮登场,有请今后会一直…

ML算法——梯度下降随笔【机器学习】

文章目录 11、梯度下降 11、梯度下降 梯度下降如何帮助参数优化? 梯度下降是一种用于参数优化的常见方法。它的基本思想是通过迭代地更新参数,以减小损失函数|代价函数的值,从而找到一个最优解。 梯度方向:→|向右|正向 ←|向左|反…

PostGIS(1):PostGIS概述

作为对象关系型数据库PostGreSQL的拓展模块,PostGIS可用于存储GIS数据,并提供了对基于GiST的R树索引支持、以及面向GIS对象的分析和处理相关的函数。 以下是PostGIS官网对其特征的介绍, (1) 先看一下百度对PostGIS的介…

Langchain-ChatGLM:基于本地知识库问答

文章目录 ChatGLM与Langchain简介ChatGLM-6B简介ChatGLM-6B是什么ChatGLM-6B具备的能力ChatGLM-6B具备的应用 Langchain简介Langchain是什么Langchain的核心模块Langchain的应用场景 ChatGLM与Langchain项目介绍知识库问答实现步骤ChatGLM与Langchain项目特点 项目主体结构项目…

php7新特性详细介绍(二)

一、PHP 7 异常 PHP 7 异常用于向下兼容及增强旧的assert()函数。它能在生产环境中实现零成本的断言,并且提供抛出自定义异常及错误的能力。 assert() 配置 | 配置项默认值可选值zend.assertions11 - 生成和执行代码 (开发模式) 0 - 生成代码,但在执…

智警杯excel和sql实训盲点

目录 excel基础操作: excel函数:智警杯赛前学习1.2--excel统计函数_lulu001128的博客-CSDN博客知识点https://blog.csdn.net/lulu001128/article/details/130936259?spm1001.2014.3001.5501 excel报表实战: excel数据透视及绘图&#xff…

Amino框架无锁算法实现并发线程安装组件(一)

Amino是无锁并行框架,线程安装,该框架封装了无锁算法,提供了可用于线程安全的一些数据结构,同时还内置了一些多线程调度模式。使用Amino进行软件开发有以下的优势: 1.对死锁的问题免疫 2.确保系统并发的整体进度 3.降低高并发下无锁竞争带…

java设计模式之:建造者模式

文章目录 建造者模式介绍建造者模式适用场景案例场景一坨坨代码实现重构代码 与工厂模式区别建造者模式优缺点总结 该说不说几乎是程序员都知道或者了解设计模式,但大部分小伙伴写代码总是习惯于一把梭。好的代码不只为了完成现有功能,也会考虑后续扩展。…

springboot自动配置源码解析

概述 使用springboog的时候引入starter就自动为我们加载,例如我们引入 spring-boot-starter-web 之后,就自动引入了 Spring MVC 相关的 jar 包,从而自动配置 Spring MVC 。 自动装配原理 SpringBootApplication SpringBootApplication: Spri…

Java的引用

一、概述 其实java有4种引用,4种可分为强、软、弱、虚。我们将从这四个方面入手进行介绍。 二、强引用 首先看到我们有一个类叫M,在这个类里我重写了一个方法叫finalize(),我们可以看到这个方法是已经被废弃的方法,为什么要重写…

【jupyter】Jupyter Notebook如何导入导出文件

目录 0.系统:windows 1.打开 Jupyter Notebook 2.Jupyter Notebook导入文件 3.Jupyter Notebook导出文件 0.系统:windows 1.打开 Jupyter Notebook 1)下载【Anaconda】后,直接点击【Jupyter Notebook】即可在网页打开 Jupyte…

用户研究干货——这一篇就够啦

一、基本概念: ①工作内容:用户研究的首要目的是帮助企业定义产品目标用户群,明确、细化产品概念,并通过对用户的任务操作特性、知觉特征、认知心理特征的研究,使用户的实际需求成为产品设计的导向,使产品…

建面超72万㎡,南山红花岭旧改规划公示,配套近15万㎡宿舍

近日,深圳市南山区城市更新和土地整备局发布关于桃源街道红花岭工业南区更新单元(暂定名)03-01、02-02地块《建设工程规划许可证》及总平面图的公告。 此次批复的红花岭工业南区02-02、03-01块,总建面超72万㎡,用地单…

nginx+tomcat 负载均衡、动静分离集群

文章目录 一、NginxTomcat负载均衡的组合原因1.1 Nginx实现负载均衡的原理1.2 Nginx实现负载均衡的主要配置项1.3 NginxTomcat负载均衡的组合的优点1.4 NginxTomcat负载均衡的实验设计 二、动静分离部署2.1 部署TOMCAT后端服务器2.2部署nginx服务器2.3安装nginx动态服务器 一、…