小工具      在线工具  汉语词典  css  js  c++  java

JavaScript深拷贝与浅拷贝

javascript,开发语言,ecmascript 额外说明

收录于:112天前

JavaScript深拷贝与浅拷贝

引言

在JavaScript中,对象的拷贝是一项常见的操作。浅拷贝和深拷贝是两种常用的拷贝方式。浅拷贝只复制对象的引用,而深拷贝创建了一个全新的对象,包含与原始对象相同的值和结构。深拷贝和浅拷贝各有适用的场景和注意事项。本文将详细介绍如何实现一个完整而优雅的深拷贝函数,处理循环引用和特殊类型,优化性能,并探讨深拷贝和浅拷贝的应用场景、注意事项和相关属性。
 

1. 深拷贝的实现

实现一个完整而优雅的深拷贝函数需要考虑以下几个方面:

1.1 基本类型和特殊类型的处理

在实现深拷贝函数时,首先需要处理基本类型(如字符串、数字、布尔值等)和特殊类型(如函数、正则表达式和日期对象等)。对于基本类型,直接返回其值即可。对于特殊类型,可以选择直接引用原始对象,而不进行复制。

function deepClone(obj) {
  // 处理基本类型
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
 
  // 处理特殊类型
  if (obj instanceof RegExp) {
    return new RegExp(obj);
  }
 
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
 
  if (obj instanceof Function) {
    return obj;
  }
 
  // 处理普通对象和数组
  const clone = Array.isArray(obj) ? [] : {};
 
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key]);
    }
  }
 
  return clone;
}

 在上述代码中,我们使用 typeof 操作符判断基本类型,根据对象的类型选择适当的处理方式。对于函数、正则表达式和日期对象,我们使用相应的构造函数创建新的实例。

1.2 处理循环引用

循环引用是指对象属性之间存在相互引用的情况,导致递归复制陷入无限循环。为了处理循环引用,我们可以使用一个额外的数据结构(如 Map 或 WeakMap)来存储已经复制的对象,以便在遇到循环引用时进行判断和处理。

下面是一个修改后的 deepClone 函数,解决了循环引用问题:

 

function deepClone(obj, map = new Map()) {
  if (typeof obj !== 'object' || obj
 
 === null) {
    return obj;
  }
 
  if (map.has(obj)) {
    return map.get(obj);
  }
 
  const clone = Array.isArray(obj) ? [] : {};
 
  map.set(obj, clone);
 
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], map);
    }
  }
 
  return clone;
}

在上述代码中,我们使用 Map 数据结构来存储已经复制的对象。在每次递归调用时,我们首先检查 map 中是否存在当前对象的引用,如果存在则直接返回对应的副本。这样,我们可以避免陷入无限循环。

1.3 性能优化

深拷贝是一项相对耗费性能的操作,特别是在处理大型对象或嵌套层次很深的对象时。为了提高性能,可以考虑以下几个优化策略:

  • 循环拷贝:使用循环代替递归,减少函数调用的开销。这可以通过迭代对象的属性并复制它们来实现。
  • 使用 JSON 序列化与反序列化JSON.stringify() 方法可以将对象序列化为字符串,JSON.parse() 方法可以将字符串解析为对象。使用这两个方法可以快速实现深拷贝,但它的适用范围受限,无法处理特殊类型(如函数和正则表达式)和循环引用。
  • 使用库函数:许多优秀的 JavaScript 库(如 Lodash、Underscore)提供了高性能的深拷贝函数。这些库经过充分测试和优化,可以满足大多数深拷贝需求。
1.4 完整的深拷贝实现示例

下面是一个完整的深拷贝函数的实现,综合考虑了上述的处理方法:

// 也可以用WeakMap优化
function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') {
    return obj;
  }
 
  if (hash.has(obj)) {
    return hash.get(obj);
  }
 
  const clone = Array.isArray(obj) ? [] : {};
 
  hash.set(obj, clone);
 
  if (obj instanceof Date) {
    return new Date(obj.getTime());
  }
 
  if (obj instanceof RegExp) {
    const flags = obj.flags;
    const pattern = obj.source;
    return new RegExp(pattern, flags);
  }
 
  if (typeof obj === 'function') {
    return cloneFunction(obj);
  }
 
  const keys = [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)];
 
  for (const key of keys) {
    clone[key] = deepClone(obj[key], hash);
  }
 
  return clone;
}
 
function cloneFunction(func) {
  const body = func.toString();
  const parameters = body.match(/\((.*?)\)/)[1];
  const functionBody = body.substring(body.indexOf('{') + 1, body.lastIndexOf('}'));
  return new Function(parameters, functionBody);
}
 
2. 浅拷贝的实现

与深拷贝不同,浅拷贝只复制对象的引用,而不创建对象的副本。下面是几种常见的浅拷贝方法:

2.1 Object.assign()

Object.assign() 方法用于将所有可枚举属性从一个或多个源对象复制到目标对象,并返回目标对象。它只会复制源对象的属性的引用,而不是属性的值。

const sourceObj = { name:
 
 'John', age: 25 };
const targetObj = Object.assign({}, sourceObj);
 
console.log(targetObj); // 输出:{ name: 'John', age: 25 }

在上述代码中,我们使用 Object.assign() 方法将源对象的属性复制到目标对象中。targetObj 是 sourceObj 的浅拷贝副本。

2.2 展开语法(Spread Syntax)

展开语法(Spread Syntax)可以用于将一个对象的所有属性展开到另一个对象中。 

const sourceObj = { name: 'John', age: 25 };
const targetObj = { ...sourceObj };
 
console.log(targetObj); // 输出:{ name: 'John', age: 25 }

在上述代码中,我们使用展开语法将源对象的所有属性展开到目标对象中。targetObj 是 sourceObj 的浅拷贝副本。

2.3 数组浅拷贝

对于数组的浅拷贝,可以使用 slice() 或展开语法。

const sourceArray = [1, 2, 3];
const targetArray1 = sourceArray.slice();
const targetArray2 = [...sourceArray];
 
console.log(targetArray1); // 输出:[1, 2, 3]
console.log(targetArray2); // 输出:[1, 2, 3]

在上述代码中,我们使用 slice() 方法和展开语法将源数组的元素复制到目标数组中。targetArray1 和 targetArray2 都是 sourceArray 的浅拷贝副本。

3. 深拷贝与浅拷贝的应用场景

深拷贝和浅拷贝各有适用的场景:

深拷贝的应用场景

  • 当需要创建一个对象的完全独立副本时,以防止对原始对象的修改。
  • 在对象状态管理中,需要创建对象的副本以记录历史状态、实现撤销和重做等操作。
  • 在数据变换和处理过程中,创建对象的副本以避免对原始数据的修改。

浅拷贝的应用场景

  • 当只需要复制对象的引用,而不需要创建对象的副本时。
  • 在一些简单的数据处理场景中,浅拷贝可以更高效地完成任务。
4. 注意事项

在使用深拷贝和浅拷贝时,需要注意以下几个问题:

  • 循环引用:深拷贝和浅拷贝都需要注意循环引用的问题。循环引用是指对象之间相互引用,导致无限循环。在处理循环引用时,深拷贝需要使用额外的数据结构(如 Map 或 WeakMap)进行记录和判断,而浅拷贝则无法解决循环引用的问题。
  • **特殊类型的处理**:在实现深拷贝和浅拷贝时,需要注意特殊类型的处理。特殊类型包括函数、正则表达式等。对于特殊类型,深拷贝可以选择直接引用原始对象,而浅拷贝只会复制引用。
  • 性能开销:深拷贝是一项相对耗费性能的操作,特别是在处理大型对象或嵌套层次很深的对象时。在实际应用中,需要根据场景权衡性能和需求。
结论

深拷贝和浅拷贝是JavaScript中常用的拷贝方式,每种方式都有其适用的场景和注意事项。通过实现一个完整而优雅的深拷贝函数,我们可以轻松地创建对象的独立副本,并处理循环引用和特殊类型。浅拷贝则提供了一种快速复制对象的方式,适用于简单的数据处理场景。根据实际需求和性能要求,选择适合的拷贝方式,可以更好地满足业务需求。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

. . .

相关推荐

额外说明

Mybatis动态分表的流程抽象

动态分表,意思就是按照一定的拆分标准,最典型的就是时间,每隔一段时间新生成一张表,或者在插入时计算此记录是否在现有表的插入区间之内,如果不在那么立即创建新表,然后将记录插入新表中。查询时根据查询条件计算记录分布的子表集合,使用UNION多表查询之后统一分

额外说明

【bat】杀掉指定端口

@echo off & setlocal EnableDelayedExpansion title 杀死端口 set /p port=请输入端口号(0~65535): set pid=0 fo

额外说明

【无标题】

继承: 子类拥有的弗雷德属性和和方法。(面向对象的三大特征之一) 子类 父类: (基类,超类) 特点: 1.子类不能继承父类的private属性和方法 2.类的及城市单继承,但是可以多重继承 格式: extends public 子类 extends 父

额外说明

html+js+jquery

HTML 重点知识:图像标签、超链接标签、表格标签、表单标签、其他标签(div、span) 重点知识:三种基本选择器、复合选择器、边框(border)、内边距(padding)、外边距(margin)、position:absolute【绝对定位】rel

额外说明

Springboot信息管理系统——后台登录功能

QQ 1274510382 Wechat JNZ_aming 商业互捧 QQ群538250800 技术搞事 QQ群599020441 技术合作 QQ群152889761 加入我们 QQ群649347320 纪年科技aming 网络安全 ,深度学习,嵌入式

额外说明

Unity WebView 插件⭐️(十三)特定模块 iOS网页视图—iOSWebViewWebView

-前言 该文章是WebView 插件系列文章,传送门:浏览器插件3D WebView专栏 上一篇文章对3D WebView 插件的 AndroidGeckoWebView 做了一个详细的介绍说明 那本篇文章就来介绍下 iOSWebViewWebView的

额外说明

C#——关键字:unsafe

C#——关键字:unsafe unsafe 关键字表示不安全上下文,该上下文是任何涉及指针的操作所必需的。 可在类型或成员的声明中使用 unsafe 修饰符。 因此,类型或成员的整个正文范围均被视为不安全上下文。 以下面使用 unsafe 修饰符声明的方

额外说明

二叉搜索树

-人生没有太晚的开始,所有的时刻都是七点。 —— 温妮 · 普赖弗曼- -作者:不能再留遗憾了 -专栏:Java学习 -本文章主要内容:明解什么是二叉搜索树以及二叉搜索树的递归和非递归查找、插入和删除。 文章目录 什么是二叉搜索树 二叉搜索树的查找 构建

额外说明

安装和配置mongobd环境变量

安装和配置mongobd环境变量,超级详细,适合新手老手安装,看步骤 第一步先从官网下载好安装包,并双击打开它 第二步 第三步 第四步 第五步 第六步 第七步 第八步 第八步 安装完成,下面配置环境变量 第一步 第二步 打开高级系统设置 第三步 点击环境

额外说明

上海交大ACM班总教头团队重磅新作,带你动手学机器学习(文末赠书4本)

目录 0 写在前面 1 什么是机器学习? 2 ACM 班总教头:俞勇 3 动手学习机器学习 赠书活动 0 写在前面 机器学习强基计划聚焦深度和广度,加深对机器学习模型的理解与应用。“深”在详细推导算法模型背后的数学原理;“广”在分析多个机器学习模型:决策

ads via 小工具