JSON.stringify 实现深度克隆的缺陷

在前端开发中,深克隆(Deep Clone)和浅克隆(Shallow Clone)是常见的操作。浅克隆和深克隆的区别主要体现在对象内部嵌套对象的处理方式上。

1. 浅克隆(Shallow Clone)

浅克隆是指创建一个新对象,但对于原对象中嵌套的对象,浅克隆只复制它们的引用,而不是创建新的对象。换句话说,浅克隆只是“浅复制”了对象的属性,嵌套的对象还是共享相同的引用。

🌰:

// 浅克隆
const shallowClone = (obj) => {return { ...obj };
};// 或者使用 Object.assign
const shallowClone2 = (obj) => {return Object.assign({}, obj);
};const obj1 = { name: 'John', address: { city: 'NY' } };
const obj2 = shallowClone(obj1);obj2.address.city = 'LA';console.log(obj1.address.city); // 输出 'LA'
console.log(obj2.address.city); // 输出 'LA'

2. 深克隆(Deep Clone)

深克隆是指创建一个新对象,并且递归地复制原对象中的所有属性,包括嵌套的对象(嵌套对象也会被克隆为新的对象)。这样,修改新对象不会影响到原对象,反之亦然。

1. 递归实现
// 深克隆实现(递归)
const deepClone = (obj) => {if (obj === null || typeof obj !== 'object') return obj;const newObj = Array.isArray(obj) ? [] : {};  // 判断是数组还是对象for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = deepClone(obj[key]);  // 递归克隆}}return newObj;
};
const obj1 = { name: 'John', address: { city: 'NY' }, hobbies: ['reading', 'gaming'] };
const obj2 = deepClone(obj1);
obj2.address.city = 'LA';
obj2.hobbies[0] = 'traveling';
console.log(obj1.address.city);  // 输出 'NY'
console.log(obj2.address.city);  // 输出 'LA'
console.log(obj1.hobbies[0]);   // 输出 'reading'
console.log(obj2.hobbies[0]);   // 输出 'traveling'
2. JSON 方法 

JSON.parse(JSON.stringify(...)) :是前端开发中常用的一种快速实现深克隆的方法,但它也有一些缺陷。

const deepCloneUsingJSON = (obj) => {return JSON.parse(JSON.stringify(obj));
};const obj1 = { name: 'John', address: { city: 'NY' }, hobbies: ['reading', 'gaming'] };
const obj2 = deepCloneUsingJSON(obj1);obj2.address.city = 'LA';
obj2.hobbies[0] = 'traveling';console.log(obj1.address.city); // 输出 'NY'
console.log(obj2.address.city); // 输出 'LA'
console.log(obj1.hobbies[0]); // 输出 'reading'
console.log(obj2.hobbies[0]); // 输出 'traveling'
3. JSON.stringify 的缺陷 

尽管其是一种快速且简单的深克隆方法,但它也有几个明显的缺陷:

1、无法克隆函数:JSON 序列化过程会丢失对象中的函数。例如:

const obj1 = { name: 'John', greet: () => console.log('Hello') };
const obj2 = JSON.parse(JSON.stringify(obj1));
console.log(obj2); // { name: 'John' }
obj2.greet(); // 报错:obj2.greet is not a function

2、无法处理 undefined:对象中的 undefined 值会被丢失,并且会在序列化过程中变成 null。

const obj1 = { value: undefined };
const obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj2.value);  // 输出: undefined

3、无法处理特殊对象:某些 JavaScript 特殊对象(如 Date、RegExp、Map、Set、Promise 等)无法正确克隆,变成普通的对象或丢失其原有属性。

const obj1 = { date: new Date(), map: new Map() };
const obj2 = JSON.parse(JSON.stringify(obj1));console.log(obj2); // { date: '2025-01-08T06:39:12.842Z', map: {} }
console.log(obj2.date instanceof Date); // 输出: false
console.log(obj2.map instanceof Map); // 输出: false

4、循环引用无法处理:如果对象包含循环引用(即一个对象的属性引用了它自身),JSON.stringify 会抛出错误。

const obj1 = {};
obj1.self = obj1;const obj2 = JSON.parse(JSON.stringify(obj1)); 
// 报错:TypeError: Converting circular structure to JSON 无法转换递归结构
4. 深度克隆的缺陷和完善

在实际开发中,基于 JSON.stringify 的深克隆方法需要处理一下。

1、处理 undefined 和 null:在序列化之前,进行预处理,避免 undefined 变成 null,或者在反序列化时进行特殊处理。

2、处理循环引用:为防止循环引用导致的错误,可以使用 WeakMap 来存储已访问的对象,并在遇到已访问的对象时直接返回,避免无限递归。

3、支持 Date、RegExp 等特殊对象:在克隆过程中判断对象的类型,使用 Object.prototype.toString.call() 来判断,进行相应的处理。

5. 更健壮的深克隆代码
// 考虑循环引用
const cache = new WeakMap(); // 确保对象在外层销毁时,Map结构自动销毁,防止内存泄露
const deepClone = (obj) => {if (obj === null || typeof obj !== 'object') return obj;// obj是对象if (cache.has(obj)) {return cache.get(obj);}const newObj = Array.isArray(obj) ? [] : {}; // 判断是数组还是对象cache.set(obj, newObj);// 考虑原型if (Object.getPrototypeOf(obj) !== Object.prototype) {// newObj.__proto__ = Object.getPrototypeOf(obj);// orObject.setPrototypeOf(newObj, Object.getPrototypeOf(obj));}for (let key in obj) {// 考虑继承属性if (obj.hasOwnProperty(key)) {newObj[key] = deepClone(obj[key]); // 递归克隆}}return newObj;
};
class Person {constructor(name, age) {this.name = name;this.age = age;}eat() {console.log('eat');}
}
Person.prototype.hobby = 'game';
Person.prototype.say = function () {console.log('say');
};
const obj = new Person('张三', 18);
// 循环引用
obj.h = obj;
console.log(obj);
console.log(deepClone(obj)); // <ref *1> Person { name: '张三', age: 18, h: [Circular *1] }

总结

1、浅克隆:只复制对象的第一层,嵌套对象还是共享引用。

2、深克隆:递归复制整个对象,包括嵌套对象。

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

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

相关文章

【软考】软件设计师

「学习路线」&#xff08;推荐该顺序学习&#xff0c;按照先易后难排序&#xff09; 1、上午题—计算机系统&#xff08;5~6分&#xff09;[1.8; ] 2、上午题—程序设计语言&#xff08;固定6分&#xff09;[1.9; ] 3、下午题—试题一&#xff08;15分&#xff09; 4、上午题—…

python学opencv|读取图像(二十九)使用cv2.getRotationMatrix2D()函数旋转缩放图像

【1】引言 前序已经学习了如何平移图像&#xff0c;相关文章链接为&#xff1a; python学opencv|读取图像&#xff08;二十七&#xff09;使用cv2.warpAffine&#xff08;&#xff09;函数平移图像-CSDN博客 在此基础上&#xff0c;我们尝试旋转图像的同时缩放图像。 【2】…

初学stm32 --- DAC输出

目录 DAC寄存器介绍&#xff08;F1&#xff09; DAC控制寄存器(DAC_CR) DAC通道1的12位右对齐数据保持寄存器(DAC_DHR12R1) DAC软件触发寄存器(DAC_SWTRIGR) DAC输出实验配置步骤 相关HAL库函数介绍 关键结构体介绍&#xff08;F1&#xff09; DAC寄存器介绍&#xff08;F1&…

Stable diffusion的SDXL模型,针不错!(含实操)

与之前的SD1.5大模型不同&#xff0c;这次的SDXL在架构上采用了“两步走”的生图方式&#xff1a; 以往SD1.5大模型&#xff0c;生成步骤为 Prompt → Base → Image&#xff0c;比较简单直接&#xff1b;而这次的SDXL大模型则是在中间加了一步 Refiner。Refiner的作用是什么呢…

51单片机——串口通信(重点)

1、通信 通信的方式可以分为多种&#xff0c;按照数据传送方式可分为串行通信和并行通信&#xff1b; 按照通信的数据同步方式&#xff0c;可分为异步通信和同步通信&#xff1b; 按照数据的传输方向又可分为单工、半双工和全双工通信 1.1 通信速率 衡量通信性能的一个非常…

【SpringAOP】Spring AOP 底层逻辑:切点表达式与原理简明阐述

前言 &#x1f31f;&#x1f31f;本期讲解关于spring aop的切面表达式和自身实现原理介绍~~~ &#x1f308;感兴趣的小伙伴看一看小编主页&#xff1a;GGBondlctrl-CSDN博客 &#x1f525; 你的点赞就是小编不断更新的最大动力 &am…

Linux之线程池与单例模式

目录 线程池 线程池代码 单例模式 饿汉模式单例模式 懒汉模式单例模式 在前几期&#xff0c;我们已经学习了多线程的创建和控制&#xff0c;学习了多线程中的同步和互斥&#xff0c;学习了多线程中的条件变量和信号量&#xff0c;基于此我们实现了基于阻塞队列和基于环形队…

青蛙云的云服务器有哪些显著优势?

青蛙云的云服务器具有以下显著优势&#xff0c;这些特点使得它成为企业构建高效IT基础设施的重要选择&#xff1a; 1.高性能硬件 青蛙云服务器采用高性能的服务器硬件进行部署&#xff0c;确保服务的稳定性和高效运行&#xff0c;为用户提供卓越的计算能力。 2. 弹性资源 支持资…

赛车微型配件订销管理系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 赛车微型配件行业通常具有产品多样性、需求不确定性、市场竞争激烈等特点。配件供应商需要根据市场需求及时调整产品结构和库存&#xff0c;同时要把握好供应链管理和销售渠道。传统的赛车微型配件订销管理往往依赖于人工经验和简单的数据分析&#xff0c;效率低下且容易…

《上古重生》V20241127111039官方中文学习版

《上古重生》官方中文版https://pan.xunlei.com/s/VODab-jcgXUAgJbj2b1-11qUA1?pwdc7di# 一款考验玩家技巧的砍杀游戏&#xff0c;从玩家到敌人&#xff0c;通通身穿古代的厚重盔甲&#xff0c;进行“重量级”的近身搏斗。在充满Cult元素的第一人称视角大冒险中杀出一条血路吧…

什么是负载均衡?NGINX是如何实现负载均衡的?

大家好&#xff0c;我是锋哥。今天分享关于【什么是负载均衡&#xff1f;NGINX是如何实现负载均衡的&#xff1f;】面试题。希望对大家有帮助&#xff1b; 什么是负载均衡&#xff1f;NGINX是如何实现负载均衡的&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源…

数组排序------冒泡排序

要求&#xff1a;给定一个数组&#xff0c;让数组升序&#xff08;降序&#xff09;排序 思路&#xff1a; 假设升序排序&#xff1a; ①&#xff1a;将数组中相邻元素从前往后依次进行比较&#xff0c;如果前一个元素比后一个元素大&#xf…

SpringBoot + 九天大模型(文生图接口)

目录 1、先到九天大模型的官网&#xff08;LLM Studio&#xff09;上订阅模型的推理服务&#xff0c;得到APIKey&#xff0c;后期需要使用它生成token才能调用模型的推理服务。 2、在SpringBoot项目里面的pom.xml文件中添加九天大模型的相关依赖&#xff0c;后面会使用到其中…

Leetcode 120. 三角形最小路径和 动态规划

原题链接&#xff1a;Leetcode 120. 三角形最小路径和 class Solution { public:int minimumTotal(vector<vector<int>>& triangle) {int n triangle.size();if (n 1)return triangle[0][0];int dp[n][n];dp[0][0] triangle[0][0];int res INT_MAX;for (in…

网络安全概论

网络安全概论--网络基础知识--防火墙技术--身份识别技术--虚拟专用网络--入侵检测技术--病毒和恶意代码 --业务连续性计划--安全管理--信息系统安全方案设计方法 一、网络安全面临的威胁 物理安全威胁、操作系统的安全缺陷、网络协议的安全缺陷、应用软件的实现缺陷、用户使…

设计模式 行为型 状态模式(State Pattern)与 常见技术框架应用 解析

状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为&#xff0c;使得对象看起来好像修改了它的类。这种设计模式的核心思想是将对象的状态和行为封装成不同的状态类&#xff0c;通过状态对象的行为改变来避免…

某团 mtgsig1.2 | sdkVersion: 3.0.0 签名算法分析记录(2025/1/9)

【作者主页】&#xff1a;小鱼神1024 【擅长领域】&#xff1a;JS逆向、小程序逆向、AST还原、验证码突防、Python开发、浏览器插件开发、React前端开发、NestJS后端开发等等 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#…

(二)最长公共子序列、最长上升子序列、最大子段和、三角形最小路径和、矩阵连乘、0-1背包

最近刚考完算法设计分析课的考试&#xff0c;复习总结一下期末考试的几道算法题吧 目录 LCR 095. 最长公共子序列 300. 最长递增子序列 53. 最大子数组和 LCR 100. 三角形最小路径和 矩阵连乘问题 0-1背包 LCR 095. 最长公共子序列 给定两个字符串 text1 和 text2&#xff…

聚类系列 (二)——HDBSCAN算法详解

在进行组会汇报的时候&#xff0c;为了引出本研究动机&#xff08;论文尚未发表&#xff0c;暂不介绍&#xff09;&#xff0c;需要对DBSCAN、OPTICS、和HDBSCAN算法等进行详细介绍。在查询相关资料的时候&#xff0c;发现网络上对于DBSCAN算法的介绍非常多与细致&#xff0c;但…

通义灵码在跨领域应用拓展之物联网篇

目录 一.引言 二.通义灵码简介 三.通义灵码在物联网领域的设备端应用 1.传感器数据采集 (1).不同类型传感器的数据读取 (2).数据转换与预处理 2.设备控制指令接收和执行 (1).指令解析与处理 (2).设备动作执行 四.通义灵码在物联网领域的云端平台应用 1.数据存储和管…