拷贝对象,拷贝快乐:揭开JavaScript中拷贝的神奇面纱

news2024/9/20 18:31:23

在这里插入图片描述

文章目录

  • 浅拷贝(Shallow Copy)
    • 1. 使用 `Object.assign()` 方法
    • 2. 使用展开运算符(Spread Operator)
    • 3. 使用数组的 `slice()` 或 `concat()` 方法
  • 深拷贝(Deep Copy)
    • 1. 使用 JSON 对象的方法
    • 2. 使用递归方法自行实现深拷贝
  • 循环引用(Circular Reference)
    • 1. 手动打破循环引用
    • 2. 使用第三方库或函数

在 JavaScript 中,深拷贝和浅拷贝是用于 复制对象的两种不同方式。
循环引用则是指一个对象内部存在对自身的引用。

浅拷贝(Shallow Copy)

浅拷贝是指创建一个新对象,并将原始对象的属性值复制到新对象中。具体而言,如果原始对象的属性是基本类型(如数字、字符串、布尔值等),则拷贝的是属性的值。但如果原始对象的属性是引用类型(如数组、对象等),则拷贝的是属性的引用,即新旧对象共享同一个引用。因此,在修改其中一个对象的属性时,另一个对象的属性也会受到影响。

下面我将详细讲解浅拷贝的实现方式和示例代码。

1. 使用 Object.assign() 方法

Object.assign() 是 JavaScript 中用于将一个或多个源对象的属性复制到目标对象的方法。它可以用于实现浅拷贝,例如将一个对象的属性复制到另一个对象。

示例代码:

let obj1 = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'music']
};

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

在上述示例中,Object.assign(target, ...sources) 接收一个目标对象和一个或多个源对象作为参数。它会将源对象的属性复制到目标对象,并返回目标对象。通过将一个空对象作为目标对象,可以实现浅拷贝,将原始对象的属性复制到新对象中。

需要注意的是,Object.assign() 只会拷贝可枚举的自有属性,不会拷贝原型链上的属性。

2. 使用展开运算符(Spread Operator)

展开运算符 ... 可以用于展开可迭代对象(如数组、字符串等),也可以用于实现浅拷贝。

示例代码:

let obj1 = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'music']
};

let obj2 = { ...obj1 };

在上述示例中,{ ...obj1 } 使用展开运算符,将原始对象的属性复制到一个新对象中,实现了浅拷贝。

类似于 Object.assign(),展开运算符也只会拷贝可枚举的自有属性,不会拷贝原型链上的属性。

3. 使用数组的 slice()concat() 方法

如果原始对象是一个数组,可以使用数组的 slice()concat() 方法来实现浅拷贝。

示例代码:

let arr1 = [1, 2, 3, 4, 5];
let arr2 = arr1.slice(); // 或者使用 arr1.concat();

// 修改 arr2 的值,不影响 arr1
arr2.push(6);

console.log(arr1); // [1, 2, 3, 4, 5]
console.log(arr2); // [1, 2, 3, 4, 5, 6]

在上述示例中,slice()concat() 方法都可以用于创建一个新的数组,其中包含了原始数组的所有元素。通过将原始数组调用这些方法后的返回值赋值给新数组,可以实现浅拷贝。

需要注意的是,浅拷贝只会复制原始对象的一层属性,对于原始对象中的嵌套对象或数组,仍然是引用关系,因此对嵌套对象的修改会影响到拷贝后的对象。

综上所述,浅拷贝是一种简单而快速的复制方式,适用于对简单对象或数组进行复制。但需要注意避免对拷贝后的对象进行深层次的修改,以免影响原始对象和其他拷贝对象。如果需要实现完全独立的拷贝,可以考虑使用深拷贝。

深拷贝(Deep Copy)

深拷贝是指创建一个新对象,将原始对象的所有属性值递归地复制到新对象中。与浅拷贝不同的是,深拷贝会创建原始对象及其嵌套对象的完全独立副本,即使对拷贝后的对象进行修改,也不会影响到原始对象或其他拷贝对象。

下面我将详细讲解深拷贝的实现方式和示例代码。

1. 使用 JSON 对象的方法

JSON 对象提供了两个方法 JSON.stringify()JSON.parse(),可以实现深拷贝。

示例代码:

let obj1 = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'music']
};

let obj2 = JSON.parse(JSON.stringify(obj1));

在上述示例中,JSON.stringify(obj1) 将原始对象转换为 JSON 字符串,JSON.parse() 方法将 JSON 字符串解析为新对象。此过程会递归复制原始对象及其嵌套对象的所有属性值。

不过需要注意的是,使用 JSON 方法进行深拷贝时,有一些特殊的数据类型无法被正确地处理,如 RegExp、Date 等。对于这些特殊类型的属性,拷贝后的结果可能不会与原始对象相同。

2. 使用递归方法自行实现深拷贝

通过递归方法,可以自行实现深拷贝。

示例代码:

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }

  let clone = Array.isArray(obj) ? [] : {};

  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key]);
    }
  }

  return clone;
}

let obj1 = {
  name: 'Alice',
  age: 25,
  hobbies: ['reading', 'music']
};

let obj2 = deepClone(obj1);

在上述示例中,deepClone() 方法通过递归调用自身,将原始对象及其嵌套对象的所有属性值复制到新对象中。对于数组和普通对象,分别创建新的空数组或空对象,然后递归复制属性值。

这种方式可以处理包括特殊类型在内的所有属性,但需要注意避免出现循环引用的情况,否则可能导致无限递归。

需要注意的是,深拷贝的性能开销较大,尤其是对于复杂嵌套的对象和大数组来说。因此,在进行深拷贝时,需要合理评估数据量和性能要求。

综上所述,深拷贝是一种创建完全独立副本的复制方式,适用于对复杂对象或嵌套对象进行复制。但需要注意避免出现循环引用的情况,并且要考虑到深拷贝的性能开销。

循环引用(Circular Reference)

循环引用是指对象的一个属性指向该对象本身,或指向对象的祖先对象链中的其他对象。在进行深拷贝时,如果遇到循环引用,会导致无限递归的情况,可能导致程序崩溃或陷入死循环。

为了避免循环引用,在实现深拷贝时需要识别并跳过已经拷贝过的对象。一种常见的方法是使用一个缓存对象来存储已经拷贝过的对象,然后在拷贝过程中检查是否已存在缓存中,如果存在则直接返回缓存中的对象引用。

循环引用是指在对象之间存在相互引用的情况,形成一个循环链,导致无法正常进行垃圾回收和内存释放。当对包含循环引用的对象进行深拷贝或遍历时,会陷入无限循环,可能导致程序崩溃或出现意料之外的结果。

下面我将详细讲解循环引用的概念、示例和解决方法。

示例:

let obj1 = {
  name: 'Alice'
};

let obj2 = {
  value: 42,
  ref: obj1
};

obj1.ref = obj2;

在上述示例中,obj1obj2 之间形成了相互引用的关系。obj2ref 属性指向 obj1,而 obj1ref 属性又指向 obj2。这样就形成了一个循环链,即循环引用。

循环引用问题的解决方法:

1. 手动打破循环引用

可以通过将其中一个对象的循环引用属性设置为 null 或者删除该属性,来手动打破循环引用关系。

示例代码:

obj1.ref = null;

或者使用 delete 操作符删除属性:

delete obj1.ref;

这样可以避免循环引用导致的问题,但可能会破坏原本对象间的关联性。

2. 使用第三方库或函数

可以使用第三方库(如 Lodash、jQuery 等)或自定义函数来处理循环引用,实现深拷贝而不受循环引用影响。

示例代码(使用 Lodash 库的 cloneDeep() 方法):

const _ = require('lodash');

let obj1 = {
  name: 'Alice'
};

let obj2 = {
  value: 42,
  ref: obj1
};

obj1.ref = obj2;

let clonedObj = _.cloneDeep(obj1);

Lodash 的 cloneDeep() 方法可以实现深拷贝并正确处理循环引用,将形成循环引用的属性设置为 undefined

需要注意的是,循环引用可能会导致程序内存泄漏,因为垃圾回收器无法释放相关的内存空间。因此,在设计数据结构时,应尽量避免出现循环引用,或者在处理循环引用时采取适当的措施,以保证程序的正常运行和内存的高效利用。

综上所述,循环引用是指对象之间存在相互引用的情况,容易导致深拷贝或遍历时的无限循环。解决循环引用问题的方法包括手动打破循环引用关系和使用第三方库或自定义函数来处理循环引用。在设计数据结构时,应尽量避免出现循环引用,以确保程序的正常运行和内存的高效利用。

以上是 JavaScript 中深拷贝、浅拷贝和循环引用的基本概念和示例代码。根据具体情况,可以选择合适的方法来实现对象的复制,并注意处理可能存在的循环引用问题。

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

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

相关文章

@validated的自定义注解校验编程式校验

自定义注解校验 前面的文章中,我们都是采用validate机制自带的条件注解来进行参数校验, 比如Min、NotNull…等等, 这些的确可以帮我们省去一部分的参数校验,可惜还有一部分的业务校验规则并不是如这般简单的, 比如前端…

C++ 程序设计入门

✅作者简介:人工智能专业本科在读,喜欢计算机与编程,写博客记录自己的学习历程。 🍎个人主页:小嗷犬的个人主页 🍊个人网站:小嗷犬的技术小站 🥭个人信条:为天地立心&…

mybatis web使用01

CREATE TABLE t_act ( id bigint auto_increment, actno varchar(255) NOT NULL, balance int(11) default 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 创建 maven web 工程 在 IntelliJ IDEA 上使用 Maven 创建 web 工程的步骤如下: 在…

机器学习-方差和偏差理论

机器学习-方差和偏差理论 关于机器学习方差和偏差的内容其实很重要,这个方差和偏差可以帮助我们去分析,模型的泛化能力和过拟合的程度。 下面我们先给存储方差和偏差的公式: 注意,下式当中, f ( x ; D ) 表示在数据集…

jmeter测并发

文章目录 jmeter是什么安装使用1、测试计划2、线程组3、添加请求4、添加报告开始测试 jmeter是什么 Apache JMeter是一个开源的负载测试工具,用于评估和分析系统的性能和功能。它可以模拟多种类型的负载,包括 Web 应用程序、数据库服务器、FTP 服务器等…

密码找回安全总结-业务安全测试实操(29)

密码重置凭证与用户账户关联不严 有些信息系统在密码找回功能的校验逻辑上存在缺陷,只校验了密码重置凭证是否在 数据库中存在,但未严格校验该重置凭证和用户账号之间的绑定关系。这种密码重置凭证 与用户账户关联不严的逻辑漏洞就让攻击者可以通过在数据包中修改用户账号达…

全志V3S嵌入式驱动开发(解决kernel 5.2.y 网卡驱动问题)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 关于v3s网卡驱动,之前我们已经实现了。而且,它还是我们移植的第一个v3s驱动,之前的内容地址在这个地方&#xf…

ATTCK(五)之ATTCK子项目invoke-atomicredteam的安装与使用

ATT&CK子项目invoke-atomicredteam的安装与使用 Invoke_atomicredteam介绍 Invoke-AtomicRedTeam 是一个 PowerShell 模块,用于执行在Red Canary 的 Atomic Red Team 项目的atomics 文件夹中定义的测试。“atomics”包含由MITRE ATT&CK™ 框架定义的每个技…

jupyter notebook优化

一.这个是jupyter notebook主题设置的相关教程,如果经常看着高亮的屏幕,对于眼睛会是一种损伤! https://blog.csdn.net/qq_41566627/article/details/104984796?utm_mediumdistribute.pc_relevant.none-task-blog-2%7Edefault%7…

[RocketMQ] Broker接收消息入口源码 (九)

文章目录 1.Broker处理请求入口1.1 registerProcessor注册消息处理器1.2 NettyServerHandler处理请求1.3 processRequestCommand分发处理请求1.3.1 rejectRequest是否拒绝请求 2.asyncProcessRequest异步处理请求2.1 asyncProcessRequest异步处理请求 3.总结 1.Broker处理请求入…

海康威视网络摄像头通过浏览器网页的配置流程

一、登录 输入网络摄像机IP地址,显示登录窗口,输入用户名和密码后,点击登录。如果是新设备,需要先激活设备,设置登录密码。(Edge浏览器如何访问海康设备?) 二、界面介绍 登入界面后…

logstash收集日志到elasticsearch

1.前言 logstash是一个相对较重的日志收集器,可以通过多种方式获取到日志数据,如tcp、日志文件、kafka、redis、rabbitmq等方式,还可以使用filter去过滤日志、转换日志为json格式,所以logstash是一个功能强大的日志收集器&#x…

六、神经网络完整训练流程(MNIST数据集为例)

一、下载数据集 MNIST数据集 将下载好的数据集解压放入同级项目路径下 二、导包 import torch import torch.nn as nnn import torch.optim as optim import torch.nn.functional as F import matplotlib.pyplot as plt import numpy as np from torchvision import datase…

华芯微特SWM34-IO速度优化之模拟SPI写速度提速

本文以在SWM34S(M33内核,150Mhz,编译器Keil MDK 5.36)上优化为例,说明优化方法和需要注意的地方,其他MCU可以参考。 在编写模拟SPI通信驱动LCD的例子的时候,会用到一个发送字节的核心函数,其基本…

【JavaSE】初步认识

目录 【1】Java语言概述 【1.1】Java是什么 【1.2】Java语言重要性 【1.3】Java语言发展简史 【1.4】Java语言特性 【1.5】 Java开发环境安装 【2】初识Java的main方法 【2.1】main方法示例 【2.2】运行Java程序 【3】注释 【3.1】基本规则 【3.2】注释规范 【4】…

ESP32-S2启动异常分析

客户反馈最近一批50块基于ESP32-S2的LoRaWAN gateway,有5块偶尔网络灯能亮,经常不能亮。 反复分析,定位,一个共同现象是用示波器看,串口输出一串信息后再没输出了。因为用了 ESP-ROM:esp32s2-rc4-20191025 Build:Oct …

企业构建知识库方案

AI模型理解误区:百万成本微调垂直行业达模型VS低成本建立企业专属知识库或ai助理_哔哩哔哩_bilibili

vscode关闭调试工具栏

问题描述 项目启动的时候老是蹦出这玩意 很碍眼 解决方案: 设置里搜索 选项改为hidden即可

前端Vue自定义注册界面模版 手机号邮箱账号输入框 验证码输入框 包含手机号邮箱账号验证

前端Vue自定义注册界面模版 手机号邮箱账号输入框 验证码输入框 包含手机号邮箱账号验证 , 下载完整代码请访问uni-app插件市场地址:https://ext.dcloud.net.cn/plugin?id13306 效果图如下:

CSS 备忘录2-动画、渐变、颜色、选择器等

1、背景 background属性是八个属性的简写形式: background-image 指定一个文件或生成的颜色渐变作为背景图片background-position 设置图片的初始位置background-size 指定背景图片的渲染尺寸background-repeat 是否平铺图片ba…