JavaScript中的浅拷贝和深拷贝

 浅拷贝

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

默认情况下:引用类型(object)都是浅拷贝

简单理解:对于对象来说,就是对最外层数据的拷贝,如果对象只有一层,则相当于深拷贝,如果对象有多层,即某个属性为引用数据类型,则拷贝的是该引用类型在堆中的地址,不会在内存创建一个新的对象。

常见的对象浅拷贝

1. Object.assign

用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

2.展开运算符... 

// 对象的浅拷贝 
// 1.Object.assign()
let obj0={a:0}
let obj2 = Object.assign(obj0,obj1)
console.log(obj0===obj2) //true
console.log(obj1===obj2) // false
obj1.name = '张三1'  //obj2不变
obj1.hobby[0]='打篮球' //obj2变了
obj1.friend.name='李四1' //obj2变了
console.log(obj1)
console.log(obj2)// 2.展开运算符
let obj3 = {...obj1}
obj1.hobby.push('摄影') //obj3会改变
console.log(obj3)

如果被拷贝对象的属性值为简单类型(如Number,String),通过Object.assign({},Obj)得到的新对象为深拷贝;如果属性值为对象或其它引用类型,则只拷贝了该引用类型的地址。 

常见的数组浅拷贝

1.Array.prototype.concat()

将alpha和numeric合并为一个新数组并返回

let arr1 = ['哈哈',18,{name:'哈利',age:10}]
let arr2 = ['嘿嘿',19,{name:'赫敏',age:11}]
// 1.concat
let arr3 = arr1.concat(arr2)
console.log(arr3===arr1) //false
console.log(arr3===arr2) // false
arr1[0]='haha' //arr3不会改
arr1[2].age=100 //arr3会改
console.log(arr3)

2. Array.prototype.slice()

slice() 方法返回一个新的数组对象,这一对象是一个由 begin 和 end 决定的原数组的浅拷贝(包括 begin,不包括end)。原始数组不会被改变。

// 1.slice截取数组
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];console.log(animals.slice(2));
// expected output: Array ["camel", "duck", "elephant"]console.log(animals.slice(2, 4));
// expected output: Array ["camel", "duck"]//2.slice浅拷贝
let arr1 = ['哈哈',18,{name:'哈利',age:10}]
let arr4 = arr1.slice() //不传参数相当于浅拷贝数组
console.log(arr4===arr1) //false
arr1[2].name = '哈利111' //arr4会变
console.log(arr4)

 3 扩展运算符 ...

// 3.展开运算符
let arr1 = ['哈哈',18,{name:'哈利',age:10}]
let arr5 = [...arr1]
arr1[2].age=5 //arr5会改变
console.log(arr5)

参考:https://github.com/YvetteLau/Step-By-Step/issues/17https://github.com/YvetteLau/Step-By-Step/issues/17

深拷贝

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

根据拷贝的层级不同可以分为浅拷贝和深拷贝,浅拷贝就是只进行一层拷贝,深拷贝就是无限层级拷贝

1.深拷贝的简单实现 JSON.parse(JSON.stringify())

// JSON.parse(JSON.stringify(obj))可以处理: 
// string,number,boolean,null,object,array
// 会忽略symbol,undefined,function,不能处理bigint,date,error,map,set
// 不能处理循环引用
let obj = {name: "哈哈",age: 18,bool: true,nu: null,un: undefined, //会忽略undefined类型的值symbol: Symbol(), //会忽略symbol类型的值// big: 15n, Do not know how to serialize a BigIntmap: new Map(), // 不能处理set: new Set(),// 不能处理obj: {color: "blue",age: 13,},arr: [1, 2, 3, 4],date: new Date(),err: new Error(),fun: function add() {}, //会忽略function类型的值
};let obj1 = JSON.parse(JSON.stringify(obj));
  • 可以处理基本数据类型(string,number,boolean,null),和引用数据类型的(对象,数组)
  • 不能处理函数,undefined,symbol,function,map,set类型的值
  • 并且当对象中包含循环引用时会报错(obj.info = obj)

3.完整的深拷贝

封装一个函数来实现:

// 可继续遍历的数据类型
const objectTag = "[object Object]";
const arrayTag = "[object Array]";
const mapTag = "[object Map]";
const setTag = "[object Set]";// 不可继续遍历的数据类型
const stringTag = "[object String]";
const numberTag = "[object Number]";
const booleanTag = "[object Boolean]";
const symbolTag = "[object Symbol]";
const dateTag = "[object Date]";
const functionTag = "[object Function]";
const errorTag = "[object Error]";// 需要深度遍历的数据类型
const deepTag = [setTag, mapTag, arrayTag, objectTag];// 工具函数-遍历数组,使用while提升性能
function forEach(arr, cb) {const len = arr.length;let i = -1;while (++i < length) {cb(arr[i], i);}return arr;
}// 工具函数-判断是否是引用类型
function isObject(value) {const type = typeof value;// 这里忽略了function类型 function类型直接返回return value !== null && type === "object";
}// 工具函数-获取数据实际类型
function getType(value) {return Object.prototype.toString.call(value);
}//工具函数-初始化被克隆的对象
function getInit(value) {const cons = value.constructor;return new cons();
}
// 工具函数-克隆symbol
function cloneSymbol(value) {return Object(Symbol.prototype.valueOf.call(value));
}// 工具函数-克隆不可遍历的类型(这里忽略了对函数和正则的处理)
function cloneOtherType(value) {const type = getType(value);const ctor = value.constructor;switch (type) {case stringTag:case numberTag:case booleanTag:case functionTag:case errorTag:case dateTag:return new ctor(value);case symbolTag:return cloneSymbol(value);default:return null;}
}
// 真正实现深拷贝的函数
function clone(value, map = new WeakMap()) {// 不是引用数据类型直接返回if (!isObject(value)) return value;// 根据数据类型做不同的处理let copyValue;const typeOfValue = getType(value);if (deepTag.includes(typeOfValue)) {copyValue = getInit(value);} else {return cloneOtherType(value);}// 处理循环引用if (map.has(value)) return map.get(value);map.set(value, copyValue);// 处理Mapif (typeOfValue === mapTag) {value.forEach((item, key) => {copyValue.set(key, clone(item, map));});return copyValue;}// 处理setif (typeOfValue === setTag) {set.forEach((item) => {copyValue.add(clone(item, map));});return copyValue;}// 处理对象if (typeOfValue === objectTag) {const keys = value.keys();forEach(keys, (item) => {copyValue[item] = clone(value[item], map);});//获取属性值为symbol类型的keysconst symbolKeys = Object.getOwnPropertySymbols(value);forEach(symbolKeys, (item) => {copyValue[item] = clone(value[item], map);});return copyValue;}// 处理数组if (typeOfValue === arrayTag) {forEach(value, (item, index) => {copyValue[index] = clone(item, map);});return copyValue;}
}

参考:如何写出一个惊艳面试官的深拷贝 (qq.com)

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

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

相关文章

Git常用命令及解释

Git是一种分布式版本控制系统&#xff0c;用于跟踪和管理代码的变化。以下是Git的常用命令及其详细解释&#xff1a; git init&#xff1a;初始化一个新的Git仓库。在项目的根目录运行该命令会在当前目录下创建一个新的Git仓库&#xff0c;并初始化版本控制。 git clone <r…

BRISK: Binary Robust Invariant Scalable Keypoints全文翻译

pdf链接&#xff1a;https://pan.baidu.com/s/1gFAYMPJStl4cF0CswY9cMQ 提取码&#xff1a;yyds 摘要 从图像中有效和高效地生成关键点是文献中深入研究的问题&#xff0c;并形成了许多计算机视觉应用的基础。该领域的领导者是SIFT和SURF算法&#xff0c;它们在各种图像转换下…

为什么说,网络安全工程师是网安行业的天花板?

为什么说&#xff0c;网络安全工程师是网安行业的天花板&#xff1f; 最近看到网上有很多人在问诸如&#xff1a;“怎样成为网络信息安全工程师”等相关问题&#xff0c;甚至还有人说“网络安全工程师已经成为这个行业的天花板”&#xff0c;这可能与近几年网络安全事件频发&a…

深度解析locked勒索病毒,勒索病毒解密,数据恢复

locked勒索病毒曾经消失了一段时间&#xff0c;但是从今年6月份以来&#xff0c;这种类型的勒索病毒又“重出江湖”&#xff0c;被感染的服务器和企业越来越多&#xff0c;这让很多企业和安全运维人员都非常头疼。为了减少这种情况的发生&#xff0c;云天数据恢复中心将对locke…

微信小程序 获取当前屏幕的可见高宽度

很多时候我们做一下逻辑 需要用整个窗口的高度或宽度参与计算 而且很多时候我们js中拿到的单位都是px像素点 没办法和rpx同流合污 官方提供了wx.getSystemInfoSync() 可以获取到部分窗口信息 其中就包括了整个窗口的宽度和高度 wx.getSystemInfoSync().windowHeight 返回值为像…

CXF调用webservice跳过https认证

1、相关依赖 compile (org.apache.cxf:cxf-spring-boot-starter-jaxws:3.5.5)2、添加配置 package com.tele.health.operation.api.listener;import org.apache.cxf.configuration.jsse.TLSClientParameters; import org.apache.cxf.transport.http.HTTPConduit; import org.…

使用 Apache Camel 和 Quarkus 的微服务(三)

【squids.cn】 全网zui低价RDS&#xff0c;免费的迁移工具DBMotion、数据库备份工具DBTwin、SQL开发工具等 Minikube&#xff1a;它可能是最简单和最容易接近的K8s集群。作为一个为了以低资源运行而设计的轻量级K8s发行版&#xff0c;有效的Minikube设置不需要除了你自己的笔记…

react-pdf | Warning: TextLayer styles not found.

问题描述&#xff1a; 使用react-pdf展示pdf&#xff0c;但是报警告&#xff0c;Warning: TextLayer styles not found. 解决方法&#xff1a; <Pageloading{"加载中..."}renderAnnotationLayer{false}renderTextLayer{false}/> 添加属性如上&#xff0c;设…

MyBatis构建SQL

1.构建SQL概述 之前通过注解开发时&#xff0c;相关 SQL 语句都是自己直接拼写的。一些关键字写起来比较麻烦、而且容易出错。 MyBatis 给我们提供了 org.apache.ibatis.jdbc.SQL 功能类&#xff0c;专门用于构建 SQL 语句 方法名说明SELECT(String.column)根据字段拼接查询…

网络原理之TCP_IP

目录 应用层重点协议 传输层重点协议 1.UDP协议 (一)UDP协议段格式 (二)UDP的特点 无连接 不可靠传输 面向数据报 全双工 缓冲区 大小受限 (三)基于UDP的应用层协议 (四)面试题 2.TCP协议 (一)TCP协议段格式 (二)TCP的特点 有连接 可靠传输 面向字节流 缓冲…

Python教程——配置环境,再探IDE

文章目录 一、Python安装下载安装验证 二、第一个Python程序常见问题 三、Python解释器四、PyCharm工具安装和配置安装使用PyCharm基本使用 一、Python安装 下载 如果我们想要使用Python语言编写程序&#xff0c;我们必须下载Python安装包并配置Python环境&#xff0c;我们现…

geecg-uniapp 源码下载运行 修改端口号 修改tabBar 修改展示数据

APP体验&#xff1a; http://jeecg.com/appIndex技术官网&#xff1a; http://www.jeecg.com安装文档&#xff1a; 快速开始 JeecgBoot 开发文档 看云视频教程&#xff1a; 零基础入门视频官方支持&#xff1a; http://jeecg.com/doc/help 一&#xff0c;下载安装 源码下载…

C++ 类和对象篇(三) 空类和6个默认成员函数

目录 一、空类 1. 是什么&#xff1f; 2. 空类中的成员 3. 空类的大小 二、6个默认成员函数 三、 构造函数 1. 构造函数是什么&#xff1f; 2. 为什么C要引入构造函数&#xff1f; 四、析构函数 1. 析构函数是什么&#xff1f; 2. 为什么要有析构函数&#xff1f; 五、拷贝构造…

win11安装双系统Ubuntu的坎坷记录

之前一直装的都是在一个硬盘中&#xff0c;这是是两块盘。 我的电脑是惠普暗影精灵8Pro 一 安装前的准备工作 1.1 记得先关闭&#xff0c;Bitlocker 输入wins&#xff0c;搜索框输入&#xff1a;设备加密设置 1.2 BIOS设置 &#xff08;惠普这电脑是开机时按 F10&#xff0…

SpringCloud(三)Sentinel、Seata、多级缓存

文章目录 Sentinel雪崩问题Sentinel与Hystrixsentinel使用案例限流规则流控模式流控效果热点参数限流 隔离和降级Feign整合Sentinel线程隔离熔断降级 授权规则与规则持续化自定义异常结果规则管理模式 Seata分布式事务问题理论基础CAP定理BASE理论 Seata架构部署TC服务微服务集…

【单片机】16-LCD1602和12864和LCD9648显示器

1.LCD显示器相关背景 1.LCD简介 &#xff08;1&#xff09;显示器&#xff0c;常见显示器&#xff1a;电视&#xff0c;电脑 &#xff08;2&#xff09;LCD&#xff08;Liquid Crystal Display&#xff09;&#xff0c;液晶显示器&#xff0c;原理介绍 &#xff08;3&#xff…

Java实现哈希表

1.哈希表定义 哈希表&#xff08;hash table&#xff0c;也叫散列表&#xff09;&#xff0c;是根据关键码值&#xff08;key value&#xff09;而直接进行访问的数据结构。也就是说&#xff0c;它通过把关键码值映射到表中一个位置来访问记录&#xff0c;以加快查找的速度。这…

浏览器唤起钉钉 各项功能

浏览器唤起钉钉对应人员聊天 文档地址 https://open.dingtalk.com/document/client/unified-routing-protocol 唤起聊天 不过只能唤起叮叮的名片 id为叮叮号 <a href"dingtalk://dingtalkclient/action/sendmsg?dingtalk_id{id}"></a>id&#xff1a; …

maven的pom.xml文件显示被删除

文章目录 1.问题情况2.问题分析3.问题解决 1.问题情况 2.问题分析 这些 pom.xml 文件被 maven 视为了忽略文件。 3.问题解决 路径&#xff1a;File --> Settings --> Build&#xff0c;Execution&#xff0c;Deployment --> Build Tools --> Maven --> Ignor…

06_Node.js服务器开发

1 服务器开发的基本概念 1.1 为什么学习服务器开发 Node.js开发属于服务器开发&#xff0c;那么作为一名前端工程师为什么需要学习服务器开发呢&#xff1f; 为什么学习服务器开发&#xff1f; 能够和后端程序员更加紧密配合网站业务逻辑前置扩宽知识视野 1.2 服务器开发可…