【JavaScript】浅拷贝与深拷贝

news2024/9/29 11:23:04

引言

浅拷贝、深拷贝是对引用类型而言的。

引用类型的变量对应一个栈区地址,这个栈区地址处存储的值是存放的真正的数据的堆区地址。

基本数据类型的变量也对应一个栈区地址,但是该地址存储的是其真正的值。

let a = b发生了什么?

在这里插入图片描述

let obj2 = obj1发生了什么?

在这里插入图片描述

JavaScript的数据类型:

什么是浅拷贝?

浅拷贝(shallow copy)创建的新对象拷贝的是原对象的属性的栈区地址。

在这里插入图片描述

图中同名变量的栈区地址相同,不同名变量的栈区地址不同。

a_ab_b都是复制了原来栈区地址的值,对_a的修改不会影响a,对_b的修改却会影响b,因为它们相当于let _b = b的关系。

什么是深拷贝?

深拷贝(deep copy)拷贝对象的堆区数据为新副本,如此新旧对象不会互相影响。

在这里插入图片描述

浅拷贝的方法有哪些?

1.JavaScript中对象的合并,Object.assign本身是浅拷贝。

const originalObject = {a:1,b:{c:1}}
const shallowCopy = Object.assign({}, originalObject);
console.log(shallowCopy === originalObject);//false,比较的是栈区地址
shallowCopy.a = 2;
shallowCopy.b.c = 2;
console.log(originalObject.a);// 1
console.log(originalObject.b.c);// 2

缺陷:Object.assign不会拷贝继承属性、不可枚举属性。

2.展开语法

let newObj = {...obj}

3.数组的cancat方法

const newArr = oldArr.concat([])

4.数组的slice方法

const newArr = oldArr.slice(start[,end]);

5.浅拷贝细致点看,是先创建一个新对象,然后将原对象的属性直接复制到新对象,所以也可以自己写一个浅拷贝函数:

function shallowCopy(obj) {
    if(obj === null || typeof obj !== 'object') return obj;
    const newObj = {};
    for (let key in obj) {// 会遍历原型链上的可枚举属性
        obj.hasOwnProperty(key) && (newObj[key] = obj[key]);
    }
    return newObj;
}
// Object.prototype.d = 1;
const obj1 = { a: 1, b: { c: 1 } };
const obj2 = shallowCopy(obj1);
obj2.a = 2;
obj2.b.c = 2;

也可以不用每次判断是否是自有属性:

function shallowCopy(obj) {
    if(obj === null || typeof obj !== 'object') return obj;
    const newObj = {};
    Object.getOwnPropertyNames(obj).forEach(key=>{
        newObj[key] = obj[key];
    })
    return newObj;
}

6.lodash库的浅拷贝方法

如何实现深拷贝?

1.JSON.stringify()JSON.parse()

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

缺陷:

  • 丢失function、undefined、Symbol这几种类型的键值对
  • NaN、Infinity的值会转为null
  • Date会变为字符串
  • RegExp会变成空对象
  • 不能拷贝不可枚举属性及原型链上的属性
  • 不能解决循环引用

2.lodash库的深拷贝方法

3.手动实现深拷贝函数基础版:

function deepClone(obj) {
    if(obj === null || typeof obj !== 'object') return obj;
    const newObj = new obj.constructor();
    for (let key in obj) {
        if (obj.hasOwnProperty(key)){
            newObj[key] = typeof obj[key] === "object" ? arguments.callee(obj[key]) : obj[key];
        }
    }
    return newObj;
}

const newObj = new obj.constructor()相比于使用{},保持了原型链的继承。

缺陷:

  • 不能处理循环引用,可能会导致堆栈溢出
  • ArrayDateRegExpMapSet对象的处理不好
  • 不能拷贝不可枚举属性和Symbol类型属性

4.手动实现深拷贝函数进阶版:

//判断是否为复杂数据类型
const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null);

const deepClone = function(obj,hash = new WeakMap()){
    if(hash.has(obj)) return hash.get(obj);

    //如果参数为Date, RegExp, Set, Map, WeakMap, WeakSet等引用类型,则直接生成一个新的实例
    let type = [Date,RegExp,Set,Map,WeakMap,WeakSet];
    if(type.includes(obj.constructor)) return new obj.constructor(obj);

    //遍历传入参数所有属性描述符
    let allDesc = Object.getOwnPropertyDescriptors(obj);
    //继承原型
    let cloneObj = Object.create(Object.getPrototypeOf(obj),allDesc);

    // 获取所有 Symbol 类型键
    let symKeys = Object.getOwnPropertySymbols(obj);
    // 拷贝 Symbol 类型键对应的属性
    if (symKeys.length > 0) {
        symKeys.forEach(symKey => {
            cloneObj[symKey] = isComplexDataType(obj[symKey]) ? deepClone(obj[symKey], hash) : obj[symKey]
        })
    }

    // 哈希表设值
    hash.set(obj,cloneObj);

    //Reflect.ownKeys(obj)拷贝不可枚举属性和符号类型
    for(let key of Reflect.ownKeys(obj)){
        // 如果值是引用类型并且非函数则递归调用deepClone
        cloneObj[key] =
            (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key],hash) : obj[key];
    }
    return cloneObj;
};

参考资料:

  • JavaScript中浅拷贝和深拷贝的区别与实现
  • JavaScript深拷贝和浅拷贝看这篇就够了
  • 关于堆栈的讲解(我见过的最经典的)
  • 深入理解js数据类型与堆栈内存
  • js中深浅拷贝的实现方式(含图解原理)
  • JavaScript深拷贝看这篇就行了!(实现完美的ES6+版本)
  • 【JavaScript】arguments.callee的作用及替换方案
  • [javascript核心-15] 手写完美深拷贝代码实现🍌
  • JS中JSON序列化JSON.stringify的坑点和处理

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

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

相关文章

MAX30102心率血氧传感器

MAX30102心率血氧传感器介绍 背景基本功能基本结构基本原理采集方法直通式采集方法反射式采集方法 血氧采集原理Beer-Lambert 定理皮肤组织模型血氧测量过程AC / DC 的计算 心率采集原理 实验结果代码走读资源链接 背景 目前,基本上所有的可穿戴式设备都集成了心率…

leetcode:190. 颠倒二进制位

一、题目: 函数原型: uint32_t reverseBits(uint32_t n) 解释:uint32是无符号int或short的别称,传入的参数是一个32位二进制串,返回值是该32位二进制串逆序后的十进制值 二、思路: 实际上并不需要真的去逆…

竞赛 深度学习 YOLO 实现车牌识别算法

文章目录 0 前言1 课题介绍2 算法简介2.1网络架构 3 数据准备4 模型训练5 实现效果5.1 图片识别效果5.2视频识别效果 6 部分关键代码7 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于yolov5的深度学习车牌识别系统实现 该项目较…

计算机网络 | OSI 参考模型

计算机网络 | OSI 参考模型 计算机网络 | OSI 参考模型应用层表示层会话层传输层网络层数据链路层物理层 参考视频:王道计算机考研 计算机网络 参考书:《2022年计算机网络考研复习指导》 计算机网络 | OSI 参考模型 OSI 参考模型自下而上分为7层&…

函数reshape(-1,)里的-1的意思

reshape函数是对narray的数据结构进行维度变换,由于变换遵循对象元素个数不变,在进行变换时,假设一个数据对象narray的总元素个数为N,如果我们给出一个维度为(m,-1)时,我们就理解为将…

6个视频素材库,免费、高清、无水印,你值得拥有~

现在做自媒体的朋友真的越来越多了,对一些视频素材的要求也越来越高,除了自己拍摄之外,还可以在网上找各种无版权视频素材,但国内高质量视频素材大多数不免费,那免费的视频素材要去哪里找呢? 今天就给大家…

探索乡村新风貌:VR全景记录乡村发展,助力乡村振兴

引言: 中国乡村正经历着巨大变革,长期以来,乡村地区一直面临着人口外流、资源匮乏等问题。然而,近年来,政府的政策支持以及新兴技术的崭露头角,如虚拟现实(VR)全景记录,…

UEFI之DXE阶段

一、DXE阶段介绍 DXE(Driver Execution Environment)阶段执行大部分系统初始化工作,进入此阶段时,内存已经可以被完全使用,因而此阶段可以进行大量的复杂工作。 DXE可以分为以下两部分: DXE内核&#xff…

Spring源码解析——AOP的使用及AOP自定义标签

正文 我们知道在面向对象OOP编程存在一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志,安全检测等,我们只有在每个对象里引入公共行为,这样程序中就产生了大量的重复代码,所以有了…

TensorFlow入门(十七、神经元的拟合原理)

深度学习的概念源于人工神经网络的研究,神经网络是由多个神经元组成,。一个神经元由以下几个关键知识点组成: ①激活函数 ②损失函数 ③梯度下降 单个神经元的网络模型如图所示 用计算公式表达如下: z为输出的结果,x为输入,w为权重,b为偏置值。z…

还在为没机器练手搭集群而苦恼?快进来免费领机器了!

前几天,在我们的技术交流群里看到有小伙伴问:有没有练手搭建Redis集群的方式推荐: 这里不禁让我想到,对于各种集群和分布式基础设施的搭建,其实是每个开发者进阶时候都要经历的一个成长过程。但是,这里对于…

数据结构与算法—时间复杂度和空间复杂度

目录 1. 什么是数据结构? 2.什么是算法? 3、算法的复杂度 4、时间复杂度 (1) 时间复杂度的概念: (2) 大O的渐进表示法: 六个例题: (3) 时间复杂度对比: 两个例题: OJ题分析时间复杂度…

一次HTTP请求流量分析详解(很详细一看就懂)

为什么要研究一个请求需要多少流量? 某天,办公室WIFI挂了,然后就开启热点用了一天,手机流量直接耗光50多个G。 后来排查发现有个任务每分钟成百上千个请求,所以才开始想到研究一下每个请求到底消耗了多少流量。以便在今…

orangepi 香橙派-实时时钟模块使用DS1307/DS3231/PCF8563

香橙派断电后,不带电池,所以时间也没法保持,需要增加模块进行时间处理 支持三种模块 DS1307/DS3231/PCF8563 在某宝上面可以买到 例子 I2cConnectionSettings settings new I2cConnectionSettings(1, Ds1307.DefaultI2cAddress); I2cDevi…

Java每日笔试题错题分析(2)

Java每日笔试题错题分析(2) 一、错题知识点前瞻第31题第24题第29题第10题 二、错题展示及其解析第31题第24题第29题第10题 一、错题知识点前瞻 第31题 三目运算符类型转换 在三目运算符java开发的环境下可以简单理解为双目运算符的类型转换问题&#xf…

基于YOLOv5、YOLOv8的烟雾报警检测(超实用项目)

目录 1.简介 2.YOLO算法 3.基于YOLOv5、YOLOv8的烟雾检测报警系统 3.1gui界面主代码 3.2YOLOv5主代码 视频已上传b站 YOLOv5、YOLOv8检测-烟雾检测报警系统_哔哩哔哩_bilibili 本文为系列专栏,包括各种YOLO检测算法项目、追踪算法项目、双目视觉、深度结构…

idea提示:无法下载源代码,找不到此对象的源代码

最近在入门消息队列,在项目里新引入了一个RabbitMQ的依赖,新建完Channel对象之后,调用该对象的方法queueDeclare想创建队列,结果发现方法里的参数都没有提示。遂点进去看,卧槽,源码下载不下来,这…

系统打印服务已关闭,竟然是它的问题!

故障现象: 一台电脑不能打印文件,一点打印就弹出对话框提示打印服务已关闭。手动开启打印服务后,大约不到1分钟打印服务又自动关闭了。 故障检修: 电脑重新启动后再次重启打印服务,打印服务依然还是会自动关闭。估计是…

【C刷题】day4

一、选择题 1、设变量已正确定义,以下不能统计出一行中输入字符个数(不包含回车符)的程序段是( ) A: n0;while(chgetchar()!\n)n; B: n0;while(getchar()!\n)n; C: for(n0;getchar()!\n…

加密市场波动:地缘政治与美股走弱引发不确定性!

伴随着国庆假期的结束,多日波动率维持低位的加密市场也似乎开始苏醒。近期多次突破28000美元未果的比特币,于9日15:00开始从27800美元附近下跌,最低跌至27260美元,同期以太坊也至1550美元左右,创近半个月来新低。 Coin…