js中的深拷贝与浅拷贝 手写深拷贝代码

news2024/11/27 19:45:00

1 什么是深拷贝和浅拷贝?

        深拷贝和浅拷贝都是复制对象时常用的两种方式,区别在于对于嵌套对象的处理,浅拷贝只复制属性的第一层属性,双方修改嵌套对象将会互相影响。深拷贝会递归复制每一层的属性,修改任意一方互不影响。

2 浅拷贝和浅拷贝的实现方法

2.1 浅拷贝的实现方法

        1.使用Object.assign()方法,接收两个参数(target, source),将source上的第一层属性复制到target的第一层属性上(不改变原有属性,如果重名将会被覆盖,常用于配置对象的修改)。

// Object.assign
// Object.assign() copies property values from a source object to a target object. For example, consider the following code:
const source = { b: 4, c: {d : 9} };
const shallowCopy = Object.assign({}, source);

shallowCopy.c.d = 10;
console.log(source); // { b: 4, c: { d: 10 } }

         2. 使用展开运算符赋值

// use ... (spread operator) for shallow copy
const source = { b: 4, c: {d : 9} };
const shallowCopy = {...source};

shallowCopy.c.d = 10;
console.log(source); // { b: 4, c: { d: 10 } }

        3. 对数组可以使用slice或者concat方法

// Array use slice or concat for shallow copy
const source = [1, 2, 3];
const shallowCopy_01 = source.slice();
// or
const shallowCopy_02 = source.concat();
shallowCopy_01[0] = 100;
shallowCopy_02[0] = 100;
console.log(source); // [1, 2, 3]

2.2 深拷贝的实现方法-包含手写实现

        方法一:使用递归手写实现,具体实现步骤如下所示:

        1. 处理基本类型和引用类型

function deepClone(value){
    // 处理基本类型(非引用类型)和null
    if(typeof value !== 'object' || value === null){
        return value
    }
}

        2.  处理日期Date对象和正则RegExp对象

// 处理正则对象
function deepClone(value){
    // ...上一步代码

    // Date对象和正则对象直接通过new的方式创建一个新的对象并返回
    if(value instanceof RegExp){
        return new RegExp(value)
    }
    if(value instanceof Date){
        return new Date(value)
    }
}

         3. 处理函数(函数一般不进行深拷贝,闭包所设计的引用过于复杂)

function deepClone(value){
    //...前面代码    

    // 处理函数 直接返回,不做处理
    if(typeof value === 'function'){
        return value
    }
}

        4.  初始化拷贝对象

// 初始化拷贝对象
function deepClone(value){
    // ...前面代码

    // 初始化拷贝对象
    const copy = Array.isArray(value) ? [] : {}
}

        5.  处理循环引用:使用WeakMap记录拷贝过的对象,防止循环引用

// 处理循环引用
function deepClone(value, cache = new WeakMap()){
    //...前面代码

    // 如果拷贝过的对象,直接返回
    if(cache.get(value)){
        return cache.get(value)
    }
    // 缓存拷贝对象
    cache.set(value, copy)
}

        6.  递归拷贝对象的属性:使用 Reflect.ownKeys 获取对象的所有属性,包括不可枚举属性和 Symbol 属性,然后递归地拷贝每个属性:

// 递归拷贝对象属性
function deepClone(value, cache = new WeakMap()){
    // ...上面的代码

    // 递归拷贝对象属性
    const keys = Reflect.ownKeys(value)
    for(let key of keys){
        copy[key] = deepClone(value[key], cache)
    }
    return copy
}

        7. 处理 Map和Set

function deepClone(value, cache = new WeakMap()){
    // ...上面代码
    // 缓存拷贝对象
    cache.set(value, copy)

    // 处理Map和Set
    if(value instanceof Map){
        const copy = new Map()
        value.forEach((val, key) => {
            copy.set(key, deepClone(val, cache))
        })
        return copy
    }

    if(value instanceof Set){
        const copy = new Set()
        value.forEach(val => {
            copy.add(deepClone(val, cache))
        })
        return copy
    }

    // ...后续代码
}
手写深拷贝完整代码
function deepClone(value, cache = new WeakMap()){
    // 处理基本类型(非引用类型)和null,直接返回即可
    if(typeof value !== 'object' || value === null){
        return value
    }

    // Date对象和正则对象直接通过new的方式创建一个新的对象并返回
    if(value instanceof RegExp){
        return new RegExp(value)
    }
    if(value instanceof Date){
        return new Date(value)
    }

    // 处理函数 直接返回,不做处理
    if(typeof value === 'function'){
        return value
    }

    // 初始化拷贝对象(保留对象原型链)
    const copy = Array.isArray(value) ? [] : Object.create(Object.getPrototypeOf(value))

    // 如果拷贝过的对象,直接返回
    if(cache.get(value)){
        return cache.get(value)
    }
    // 缓存拷贝对象
    cache.set(value, copy)

    // 处理Map和Set
    if(value instanceof Map){
        const copy = new Map()
        value.forEach((val, key) => {
            copy.set(key, deepClone(val, cache))
        })
        return copy
    }

    if(value instanceof Set){
        const copy = new Set()
        value.forEach(val => {
            copy.add(deepClone(val, cache))
        })
        return copy
    }

    // 递归拷贝对象属性(包含不可枚举属性)
    const keys = Reflect.ownKeys(value)
    for(let key of keys){
        copy[key] = deepClone(value[key], cache)
    }

    return copy
}

         方法二:使用JSON.parse(JSON.stringify(obj)),但是会造成部分数据丢失,且无法处理特殊对象。

// deep copt use Json.parse and Json.stringify
const source = { b: 4, c: {d : 9} };
const deepCopy = JSON.parse(JSON.stringify(source));
深拷贝测试示例
const obj = {
  num: 1,
  str: 'string',
  bool: true,
  nullValue: null,
  undefinedValue: undefined,
  symbol: Symbol('sym'),
  date: new Date(),
  regExp: /\w+/g,
  func: function () { console.log('function'); },
  arr: [1, 2, { a: 3 }],
  obj: { x: 10, y: { z: 20 } },
  map: new Map([['key1', 'value1'], ['key2', { a: 1 }]]),
  set: new Set([1, 2, { b: 3 }]),
  [Symbol('symbolKey')]: 'symbolValue',
};

obj.circularRef = obj; // 添加循环引用

const clonedObj = deepClone(obj);

console.log(clonedObj);

        测试结果如下所示:

 

3 深拷贝需要注意的问题

        实现深拷贝时需要考虑以下问题:

  • 循环引用问题:如果对象内部引用自己,不考虑该情况将会导致无限递归。
  • 性能损耗:深拷贝大型对象和数组将会浪费大量性能,带来卡顿。
  • 考虑特殊对象类型:例如Date RegExp Set Map Function等对象需要特殊处理
  • 不可枚举属性和原型链:只会复制对象的可枚举属性
  • 某些深拷贝造成的数据丢失:使用JSON.parse(JSON.stringify(obj))时造成undefined Symbol Funcion等类型数据的丢失

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

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

相关文章

YOLO11项目实战1:道路缺陷检测系统设计【Python源码+数据集+运行演示】

一、项目背景 随着城市化进程的加速和交通网络的不断扩展,道路维护成为城市管理中的一个重要环节。道路缺陷(如裂缝、坑洞、路面破损等)不仅影响行车安全,还会增加车辆的磨损和维修成本。传统的道路缺陷检测方法主要依赖人工巡检…

[云服务器17] 搭建PMail个性邮箱!我的邮箱我做主

哈喽大家好啊! 我们先来看一个邮箱: 123456example163.com你发现了吗?后面有163的域名! 这个就标志了邮箱服务提供商的名字,像常见的Outlook 163等。 那么作为一个追求自由主义的人,今天,我们就要使用开…

exe4j安装使用教程

A-XVK258563F-1p4lv7mg7sav A-XVK209982F-1y0i3h4ywx2h1 A-XVK267351F-dpurrhnyarva A-XVK204432F-1kkoilo1jy2h3r A-XVK246130F-1l7msieqiwqnq A-XVK249554F-pllh351kcke50

出口企业财务管理,六款热门产品测评与推荐

本文介绍了ZohoBooks、QuickBooks、Xero等6款外贸管理软件,各有特点如全球化管理、移动应用、自动对账等,适合不同出口企业需求。选择时应考虑企业规模、业务复杂度和预算,建议先试用再购买。 一、Zoho Books Zoho Books是一款适合外贸企业跨…

【C++】迭代器失效问题解析

✨ Blog’s 主页: 白乐天_ξ( ✿>◡❛) 🌈 个人Motto:他强任他强,清风拂山冈! 🔥 所属专栏:C深入学习笔记 💫 欢迎来到我的学习笔记! 一、迭代器失效的概念 迭代器的作用…

每日OJ题_牛客_游游的水果大礼包_枚举_C++_Java

目录 牛客_游游的水果大礼包 题目解析 C代码 Java代码 牛客_游游的水果大礼包 游游的水果大礼包 (nowcoder.com) 描述: 游游有n个苹果,m个桃子。她可以把2个苹果和1个桃子组成价值a元的一号水果大礼包,也可以把1个苹果和2个桃子…

GO网络编程(二):客户端与服务端通信【重要】

本节是新知识,偏应用,需要反复练习才能掌握。 目录 1.C/S通信示意图2.服务端通信3.客户端通信4.通信测试5.进阶练习:客户端之间通信 1.C/S通信示意图 客户端与服务端通信的模式也称作C/S模式,流程图如下 其中P是协程调度器。可…

【Qt】控件概述——按钮类控件(2)

控件概述(2) 1. PushButton2. RadioButton——单选按钮2.1 使用2.2 区分信号 clicked,clicked(bool),pressed,released,toggled(bool)2.3 QButtonGroup分组 3. CheckBox——复选按钮 1. PushButton QPushB…

写不出论文?分享7款写论文的ai免费工具网站

在当今学术研究和写作领域,撰写高质量的论文是一项挑战性的任务。幸运的是,随着人工智能技术的发展,AI论文写作工具逐渐成为帮助学者和学生提高写作效率的重要工具。这些工具不仅能够提高写作速度,还能通过智能校对和优化&#xf…

【大数据】Doris 数据库与表操作语法实战详解

目录 一、前言 二、数据库基本操作 2.1 修改账户密码 2.2 创建新用户 2.3 创建数据库与账户授权 2.3.1 数据库创建补充说明 2.3.2 数据库账户赋权 三、数据表基本操作 3.1 Doris 数据表介绍与使用 3.1.1 建表结构说明 3.1.2 建表语法与操作 3.1.3 建表示例 - 单分区…

Android KMP 快速入门2 - Koin依赖注入

这里写目录标题 代码仓库KMP 框架基本框架actual&expectKoin 依赖注入管理 代码仓库 本小节代码已经上传到gitee,请自行查看: 点击访问仓库 KMP 框架 基本框架 源码集合描述存放内容示例androidMain针对 Android 平台的代码使用 Android SDK、Andr…

Python、C++、java阶乘算法

最近,我除了Python还学了C和Java,然后在网上看到编程考题:阶乘。 首先,我们先理解什么是阶乘。 阶乘是数学中的一个概念,通常定义为从1乘到指定的数。具体来说,一个正整数的阶乘(记作n!&#…

【课程学习】Wireless Communications

Goldsmith A. Wireless communications[M]. Cambridge university press, 2005. Wireless Communications 无线通信课程 文章目录 2-Path Loss, Shadowing, and Multipath2.4-Two-Ray Multipath Model时延扩展 delay spread P33 3-Statistical Multipath Channel Models3.3-Wid…

Python+Matplotlib创建高等数学上册P2页例3交互动画

import numpy as np import matplotlib.pyplot as plt from matplotlib.widgets import Slider from matplotlib.patches import Rectangle# 创建图形和坐标轴 fig, ax plt.subplots(figsize(12, 8)) plt.subplots_adjust(bottom0.2)# 设置坐标轴范围 ax.set_xlim(-2*np.pi, 2…

BugReport中的App Processor wakeup字段意义

一、功耗字段意义: App processor wakeup:Netd基于xt_idletimer 待机下监视网络设备的收发工作状态,即当设备发生联网从休眠态变成为唤醒态时,会记录打醒者的uid(uid大于0)和网络类型(wifi或数据类型)、时间戳 实际日志:我们在B…

【C++复习】C++11经典语法

文章目录 {}列表初始化1. 初始化内置类型变量2. 初始化数组3. 初始化标准容器4. 初始化自定义类型5. 构造函数初始化列表6. 初始化列表(initializer_list)7. 返回值初始化8. 静态成员变量和全局变量的就地初始化9. 防止类型收窄总结 decltype右值引用完美…

图像处理案例04

图像处理 问题:把不规则的图片按照参考图摆放 步骤: 1. 用ORB找关键点 2. 关键点匹配 3. 根据上一步匹配的关键点得出单应性矩阵 4. 根据单应性矩阵对不规则进行透视变换 import cv2 import numpy as np import matplotlib.pyplot as pltimgl cv2.imrea…

精华帖分享 | 因子构建思考1

本文来源于量化小论坛股票量化板块精华帖,作者为z-coffee。 以下为精华帖正文: 一段时间没写帖子,其实一直在研究策略,只是从不同的角度去思考而已。熟悉我的老板其实清楚,我的炉子水平一般,基本不太依托…

什么是 Web 应用中的 Facet 控件

在 Web 页面设计和开发中,facet 是一个十分重要的概念,尤其在电子商务、数据搜索和筛选功能中非常常见。Facet 通常指的是一种分类或过滤的方式,用于让用户能够通过多维度的条件来细化和调整数据结果,从而找到更符合需求的内容。F…

资源《Arduino 扩展板5-单电机驱动》说明。

资源链接: Arduino 扩展板5-单电机驱动 1.文件明细: 2.文件内容说明 包含:AD工程、原理图、PCB。 3.内容展示 4.简述 该文件为PCB工程,采用AD做的。 该文件打板后配合Arduino使用,属于Arduino的扩展板。