当前位置: 首页 > news >正文

js深拷贝与浅拷贝

 1.浅拷贝概念

浅拷贝是其属性与拷贝源对象的属性共享相同引用,当你更改源或副本时,也可能(可能说的是只针对引用数据类型)导致其他对象也发生更改。

特性

  • 会新创建一个对象,即obj===obj2返回fasle;
  • 对象中如果是基本数据类型会单独开辟一份空间存储,如果是引用数据类型则共用一个内存地址。即如果是基本数据类型新对象修改不会造成源对象值改变,但是如果是引用数据类型,修改会造成源对象属性同时被修改

2.var people1 = people不是浅拷贝

注意var people1 = people不是浅拷贝。只是对内存地址进行赋值,people1 === people 返回true,这种不是浅拷贝

// 这个种方式不是浅拷贝
var people = {name: 'allen'
}// 这里只是对内存地址进行赋值,people1 === people 返回true,这种不是浅拷贝
var people1 = people
people1.name = 'jason'

3.浅拷贝示例

浅拷贝:会新创建一个对象,对源对象的属性进行拷贝,使两者共享同一个地址。obj === obj2返回false。比如:var obj2 = Object.assign({}, obj)

说明:Object.assign()是浅拷贝,会生成一个新的对象浅拷贝对于基本数据类型a,会重新单独开辟一份空间进行存储, 而对于引用数据类型b拷贝的是其内存地址,所以obj2和obj对于拷贝后的b是共用一份内存地址。所以如果修改obj2中b的b1的值,则obj中的b1的值也会改变

4.浅拷贝的方法(Object.create(),Object.assign(),扩展运算符)

4.1Object.create()

创建一个对象,会将源对象放到新对象的原型上(即会将obj对象放到obj2的原型上),返回一个新的对象。

var obj = {a: 1}
var obj2 = Object.create(obj)
obj.a = 2 //修改基本数据类型,源对象也会改变
console.log(obj === obj2); //false
console.log(obj,obj2);

4.2 Object.assign()

将对象属性进行合并,并返回新的对象,遇到重名对象会进行覆盖

var obj = {a: 1,b: {b1: 1}
}
var obj2 = Object.assign({}, obj)
obj.a = 2
obj.b.b1 = 2console.log(obj === obj2);
console.log(obj, obj2);

结果:源对象基本数据类型值没有改变,引用数据用的同一个会改变

4.3扩展运算符

var obj = {a: 1,b: {b1: 1}
}var obj2 = {...obj};
obj2.a = 3;
obj2.b.b1 = 2;console.log(obj === obj2);
console.log(obj, obj2);

 结果:同上

5.深拷贝概念

深拷贝是指其属性与其拷贝的源对象的属性不共享相同的引用,当你更改源或副本时,可以确保不会导致其他对象也发生更改

6.深拷贝的方法

6.1JSON.parse(JSON.stringify())

原理:通过改变数据类型的形式进行深拷贝,JSON.stringify()将对象序列化成字符串,而JSON.parse()将字符串转为对象。注意不是JSON.stringify()是两个方法组合使用

var obj = {a: 1,b: {b1: 1}
}
var obj1 = JSON.parse(JSON.stringify(obj))
obj1.a = 2
obj1.b.b1 = 2
console.log(obj === obj2);
console.log(obj, obj2);

结果:基本数据类型和引用数据类型的改变都不会印象源对象 

6.2 Lodash.cloneDeep()

cdn地址 : https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js

import _ form "lodash";var obj = {a: 1,b: {b1: 1}
}
var obj1 = _.cloneDeep(obj)
obj1.a = 2
obj1.b.b1 = 2

7.深拷贝的原理

7.1主要考虑以下三个方面:

  1. 数据类型的划分
  2. 递归处理
  3. 循环引入的处理:循环引入即对象可以一直展开,无限嵌套,无限循环

7.1循环引入:

var obj = {};
obj.a = obj;
console.log(obj);

对象可以一直展开,无限嵌套,无限循环

 8.手动实现深拷贝

 8.1方式一:简单实现(只判断数据基本类型和针对对象数据结构)

  • 判断数据类型,特殊类型直接返回;
  • 遍历对象进行递归处理; 
        function deepClone(obj) {console.log(typeof obj);// 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {return obj;}if (obj instanceof Object) {const newObj = {};for (let key in obj) {newObj[key] = deepClone(obj[key]);}// 注意是最后return不是在for循环中returnnewObj[key]return newObj;}}var obj = {a: 1,b: {b1: 1}}var obj2 = deepClone(obj);obj2.a = 2;obj2.b.b1 = 2console.log(obj === obj2);console.log(obj, obj2);

 

8.2 方式二(增强): 针对Map、Set、Array等结构进行深拷贝处理

  •  判断数据类型,特殊类型直接返回;
  • 遍历对象进行递归处理; 
  • 对Map,Set,Array数据结构进行处理
  • 注意Map,Set,Array各自不同遍历及修改的方法
        function deepClone(obj) {// 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {return obj;}if (obj instanceof Set) { //对于Set数据结构(for of 循环;newSet.add(val)) const newObj = new Set();for (let val of obj) {newObj.add(deepClone(val));}return newObj;} else if (obj instanceof Map) { //对于Map数据结构(for of循环 map.set(key,val))const newObj = new Map();for (let [key, val] of obj) {newObj.set(key, deepClone(val));}return newObj;} else if (obj instanceof Array) { //对于数组数据结构(使用forEach进行循环push进行添加)const newObj = [];obj.forEach(item => {newObj.push(deepClone(newObj[item]));});return newObj} else if (obj instanceof Object) {const newObj = {};for (let key in obj) {newObj[key] = deepClone(obj[key]);}// 注意是最后return不是在for循环中returnnewObj[key]return newObj;} else {return obj;}}var newMap = new Map();newMap.set('key1', 12);newMap.set('key2', 13);var newSet = new Set();newSet.add('11');newSet.add('22');var obj = {a: 1,b: {b1: 1},c: {c1: [1, 2, 3]},d: newMap,f: newSet}var obj2 = deepClone(obj);obj2.a = 2;// 基本数据类型修改值obj2.b.b1 = 2// 数组改变值obj2.c.c1 = [2, 3, 4];// map集合修改key1obj2.d.set('key1','更改后的key1');// Set集合增加一个obj2.f.add('set集合增加的值');console.log(obj === obj2);console.log(obj, obj2);

8.3 方式三(循环引用的处理)

  •  判断数据类型,特殊类型直接返回;
  • 遍历对象进行递归处理; 
  • 对Map,Set,Array数据结构进行处理
  • 注意Map,Set,Array各自不同遍历及修改的方法

如果有循环引用,使用方式二深拷贝处理,会一直循环导致内存溢出。map = new WeakMap()用于判断是否已经有过相同引用

        function deepClone(obj, map = new WeakMap()) {// 判断如果为null,不等于字符串的object,function, 为Date类型,RegRex类型则直接返回if (obj === null || typeof obj !== 'object' || typeof obj === 'function' || obj instanceof Date || obj instanceof RegExp) {return obj;}// 循环引入处理(如果obj下的key的值为obj本身,则循环将obj的值设置到map的key中)// WeakMap的key只能是对象,Set、Map、Array、Object每个都设置map.set(obj,newObj);是因为每种数据结构都有可能存在循环引用if (map.get(obj)) {return map.get(obj);}if (obj instanceof Set) { //对于Set数据结构(for of 循环;newSet.add(val)) const newObj = new Set();map.set(obj, newObj);for (let val of obj) {newObj.add(deepClone(val, map));}return newObj;} else if (obj instanceof Map) { //对于Map数据结构(for of循环 map.set(key,val))const newObj = new Map();map.set(obj, newObj);for (let [key, val] of obj) {newObj.set(key, deepClone(val, map));}return newObj;} else if (obj instanceof Array) { //对于数组数据结构(使用forEach进行循环push进行添加)const newObj = [];map.set(obj, newObj);obj.forEach(item => {newObj.push(deepClone(newObj[item], map));});return newObj} else if (obj instanceof Object) {const newObj = {};map.set(obj, newObj); //此处设置后,后面newObj即使更改,map内存地址使用的是一个所以map的值也会跟着变for (let key in obj) {newObj[key] = deepClone(obj[key], map);}// 注意是最后return不是在for循环中returnnewObj[key]return newObj;} else {return obj;}}// 循环引入var obj3 = {};obj3.a = obj3;var obj4 = deepClone(obj3);obj4.b = obj4;console.log(obj3 === obj4);console.log(obj3, obj4);

相关文章:

js深拷贝与浅拷贝

1.浅拷贝概念 浅拷贝是其属性与拷贝源对象的属性共享相同引用,当你更改源或副本时,也可能(可能说的是只针对引用数据类型)导致其他对象也发生更改。 特性: 会新创建一个对象,即objobj2返回fasle&#xf…...

Docker-harbor私有仓库部署与管理

搭建本地私有仓库 #首先下载 registry 镜像 docker pull registry #在 daemon.json 文件中添加私有镜像仓库地址 vim /etc/docker/daemon.json { "insecure-registries": ["20.0.0.50:5000"], #添加,注意用逗号结…...

ArcGIS笔记8_测量得到的距离单位不是米?一经度一纬度换算为多少米?

本文目录 前言Step 1 遇到测量结果以度为单位的情况Step 2 简单的笨办法转换为以米为单位Step 3 拓展:一经度一纬度换算为多少米 前言 有时我们会遇到这种情况,想在ArcGIS中使用测量工具测量一下某一段距离,但显示的测量结果却是某某度&…...

SpringBoot入门详解

目录 因何而生的SpringBoot 单体架构的捉襟见肘 SpringBoot的优点 快速入门 高曝光率的Annotation SpringBoot的工作机制 了解SpringBootApplication SpringBootConfiguration EnableAutoConfiguration 自动配置的幕后英雄:SpringFactoriesLoader Compon…...

数据分析案例-基于snownlp模型的MatePad11产品用户评论情感分析(文末送书)

🤵‍♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞&#x1f4…...

Leetcode刷题解析——904. 水果成篮

1. 题目链接:904. 水果成篮 2. 题目描述: 你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。 你想要尽可能多地收集水果。然而,农场的主…...

Spring Boot RESTful API

学习到接口部分了,记录一下 关于restful api感觉这篇文章讲的十分详细且通俗易懂一文搞懂什么是RESTful API - 知乎 (zhihu.com) Spring Boot 提供的 spring-boot-starter-web 组件完全支持开发 RESTful API ,提供了 GetMapping:处理get请求…...

k8s day04

昨日内容回顾: - configMap ---> cm 应用场景: 主要用于配置文件的持久化。 - secret 应用场景: 存储敏感数据,并非加密数据。 - pod探针(probe): - livenessProbe: 健康检查探针&#x…...

ESP32-IPS彩屏ST7789-Arduino-简单驱动

目的: 使ESP32能够驱动点亮ST7789显示屏 前提条件: ESP32 ST7789 (240 x240,IPS) 杜邦线 Arduino 过程: 0x00--接线 0x01--驱动: 彩屏驱动库 针对不同的彩屏驱动芯片,常用的 Arduino…...

高效工具类软件使用

高效工具类软件使用 目录概述需求: 设计思路实现思路分析1.Leanote2.Obsidian 的使用 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for…...

批处理文件(.bat)中,dir与tree命令的效果

目录 dir命令 用法 操作 效果 dir /? dir dir D:\111\111_3 dir D:\111 *.mp4 dir D:\111 /ad dir D:\111 /ar dir D:\111 /s dir D:\111\111_3 >1bat.txt dir D:\111 >>1bat.txt tree命令 用法 操作 效果 tree /? tree tree D:\111\111_3 tree…...

STM32 ---- 再次学习STM32F103C8T6/STM32F409IGT6

目录 一、环境搭建及介绍 关于STM32基础介绍 新建工程 外设案例 LED流水灯 蜂鸣器 上拉电阻和下拉电阻知识 电压比较器 c语言基础知识 类型、结构体、枚举 类型int8_t int16_t int32_t 宏替换 #define 和typedef用法 结构体两种填充方法 和 命名规则 枚举用法 常用…...

UE4 EQS环境查询 学习笔记

EQS环境查询对应Actor的范围 EQS环境查询查询对应的类 查询到即有一个蓝色的球在Actor上,里面有位置信息等等 在行为树运行EQS,按键(‘)可以看到Player的位置已经被标记 运行对应的EQS在这里放如EQS就可以了 Generated Point&…...

计算机算法分析与设计(11)---贪心算法(活动安排问题和背包问题)

文章目录 一、贪心算法概述二、活动安排问题2.1 问题概述2.2 代码编写 三、背包问题3.1 问题描述3.2 代码编写 一、贪心算法概述 1. 贪心算法的定义:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以…...

shell命令以及运行原理

Linux严格意义上说的是一个操作系统,我们称之为“核心(kernel)“ ,但我们一般用户,不能直接使用kernel。 而是通过kernel的“外壳”程序,也就是所谓的shell,来与kernel沟通。如何理解&a…...

MySQL进阶(再论JDBC)——JDBC编程思想的分析 JDBC的规范架构 JDBC相关的类分析

前言 SQL(Structured Query Language)是一种用于管理关系型数据库的标准化语言,它用于定义、操作和管理数据库中的数据。SQL是一种通用的语言,可以用于多种关系型数据库管理系统(RDBMS),如MySQ…...

rabbitMQ的知识点

RabbitMQ是一种消息队列软件,它实现了高度可靠的消息传递机制。RabbitMQ支持多种消息协议,包括AMQP、STOMP、MQTT等,比较灵活。以下是一些rabbitmq的知识点: 1. 消息队列:消息队列是一种分布式系统中广泛使用的通信模…...

​EtherNet/IP 库卡机器人和EtherCAT倍福PLC总线协议连接案例​

EtherNet/IP 是一种适合于工业环境和对时间要求比较苛刻的应用的网络。而远创智控YC-EIPM-ECT通讯网关,是一款自主研发的EtherNet/IP 从站功能的通讯网关。它不仅可以实现EtherNet/IP 和EtherCAT的无缝连接,还可以将EtherNet/IP 作为从站连接到EtherCAT总…...

微信小程序 uniapp+vue线上洗衣店业务管理系统演89iu2

本课题意在设计一种系统的、基于用户体验的线上洗衣服务模式,具有如下的研究意义: (1)为用户提供更简单、便捷的洗衣服务模式; (2)为智能柜的盈利模式提供了新的方向; (3)通过线上系统、智能柜与洗衣工厂结合的方式,为洗衣企业构建了一套节 省人力成本的…...

Maven项目,进行编译,使用idea的 编译功能,就是正常的,但是在终端中执行 mvn clean compile 报错

一、背景: Maven项目,进行编译,使用idea的 编译功能,就是正常的,但是在终端中执行 mvn clean compile 报错 报错信息: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin…...

linux之kylin系统nginx的安装

一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

linux 下常用变更-8

1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行&#xff0c;YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID&#xff1a; YW3…...

leetcodeSQL解题:3564. 季节性销售分析

leetcodeSQL解题&#xff1a;3564. 季节性销售分析 题目&#xff1a; 表&#xff1a;sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化

缓存架构 代码结构 代码详情 功能点&#xff1a; 多级缓存&#xff0c;先查本地缓存&#xff0c;再查Redis&#xff0c;最后才查数据库热点数据重建逻辑使用分布式锁&#xff0c;二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...