JavaScript深拷贝与浅拷贝的全面解析

深拷贝和浅拷贝的核心区别在于是否递归复制引用类型。浅拷贝仅复制对象第一层属性,引用类型共享内存,修改新对象会影响原对象,常见方法有Object.assign、扩展运算符、slice等;深拷贝则完全复制所有层级,新旧对象独立,互不影响。实现方式包括JSON.parse(JSON.stringify())(适用于纯数据)、手动递归(支持循环引用和内置对象)、structuredClone()(现代浏览器原生支持)及Lodash的cloneDeep()。选择时需权衡兼容性、性能与使用场景,避免对DOM节点或window对象进行深拷贝。

javascript深拷贝与浅拷贝的全面解析

JavaScript中的深拷贝和浅拷贝是处理对象复制时必须理解的核心概念。很多人在操作对象时,以为赋值就是“复制”,结果修改新对象时原对象也被改变了——这就是浅拷贝带来的副作用。下面从原理到实现,全面解析两者的区别与使用方式。

什么是浅拷贝

浅拷贝是指创建一个新对象,这个新对象拥有原对象属性的副本,但属性值如果是引用类型(如对象、数组),则只复制了指向该对象的引用,而不是其本身。

换句话说:基本数据类型会被复制,而引用类型只是“共享”同一个内存地址。

常见浅拷贝方法包括:

立即学习“Java免费学习笔记(深入)”;

Object.assign():只能实现一级属性的浅拷贝 扩展运算符 {…obj}:语法简洁,效果同 Object.assign Array.prototype.slice()concat():用于数组浅拷贝注意:这些方法都无法深入嵌套对象进行复制。

示例:

const original = { name: 'Alice', info: { age: 25 } };const shallow = { ...original };shallow.info.age = 30;console.log(original.info.age); // 输出 30 —— 原对象被影响

什么是深拷贝

深拷贝会递归地复制对象的所有层级,包括嵌套的对象和数组,确保新对象与原对象完全独立,互不影响。

深拷贝的关键在于:每一个引用类型都重新创建一份副本,断开与原数据的联系。

实现深拷贝的方法有多种,各有优劣:

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

这是最简单的深拷贝方法,适用于纯数据对象(不含函数、undefined、Symbol、Date 等特殊类型)。

标贝科技 标贝科技

标贝科技-专业AI语音服务的人工智能开放平台

标贝科技 14 查看详情 标贝科技

const deep = JSON.parse(JSON.stringify(original));

缺点:无法处理函数、undefined、Symbol Date 类型会被转为字符串 循环引用会报错 RegExp、Error 对象会丢失信息

2. 手动递归实现

通过判断数据类型,逐层复制,是最可控的方式。

function deepClone(obj, seen = new WeakMap()) {  if (obj === null || typeof obj !== 'object') return obj;    if (seen.has(obj)) return seen.get(obj); // 防止循环引用    let clone;  if (obj instanceof Date) {    clone = new Date(obj);  } else if (obj instanceof RegExp) {    clone = new RegExp(obj);  } else if (Array.isArray(obj)) {    clone = obj.map(item => deepClone(item, seen));  } else {    clone = {};    for (let key in obj) {      if (obj.hasOwnProperty(key)) {        clone[key] = deepClone(obj[key], seen);      }    }  }    seen.set(obj, clone);  return clone;}

这种方式支持循环引用,兼容大部分内置对象,适合需要精确控制的场景。

3. 使用结构化克隆算法(浏览器API)

现代浏览器提供 structuredClone() 方法,可安全深拷贝支持的数据类型。

const cloned = structuredClone(original);

支持:对象、数组、Date、RegExp、Map、Set、Blob、FileList、ImageData 等。

优势:原生支持、安全、无需引入库。
限制:不支持函数、不能跨 iframe/window 完全通用。

4. 使用第三方库(如 Lodash)

Lodash 提供 _.cloneDeep() 方法,功能强大且稳定。

const _ = require('lodash');const deep = _.cloneDeep(original);

适合复杂项目,处理边界情况更完善。

如何选择合适的拷贝方式

根据实际需求权衡性能与兼容性:

如果对象是纯数据(如配置、JSON响应),用 JSON.parse + stringify 最快 需要兼容 Date、RegExp 或存在嵌套结构,推荐 structuredClone(现代环境) 需支持函数或特殊类型,使用 手动递归Lodash 仅复制第一层,可用 扩展运算符Object.assign

特别提醒:不要对 DOM 节点、window 对象等使用深拷贝,会导致意外行为或错误。

基本上就这些。理解深浅拷贝的本质,能避免很多隐蔽的 bug,尤其是在状态管理、表单编辑、撤销功能等场景中尤为重要。

以上就是JavaScript深拷贝与浅拷贝的全面解析的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/288766.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月4日 19:42:11
下一篇 2025年11月4日 19:43:29

相关推荐

  • Go语言:安全高效地将字符串解析为浮点数

    本文详细介绍了go语言中如何将字符串转换为浮点数(float64)。核心在于使用strconv.parsefloat函数,并强调了正确处理其返回的错误。通过一个常见的错误示例,我们展示了如何通过将条件if err != nil修正为if err == nil来确保只有成功的转换才被添加到结果集中,从…

    好文分享 2025年12月16日
    000
  • 如何在Golang中使用os操作文件与目录_Golang os文件目录操作方法汇总

    Go语言中os包提供文件与目录操作,如创建、打开、删除、重命名、遍历等。1. os.Create创建文件,os.Mkdir/MkdirAll创建目录;2. os.Open/OpenFile打开文件或目录;3. os.Remove/RemoveAll删除文件或目录;4. os.Stat获取文件信息;5…

    2025年12月16日
    000
  • Golang如何开发简单的问卷调查项目

    答案是使用Golang搭建一个简易问卷系统,通过定义Survey和Response结构体,实现展示问卷、提交回答和查看结果的完整流程。 用Golang开发一个简单的问卷调查项目,核心是搭建HTTP服务、设计数据结构、处理表单提交和展示结果。整个过程不复杂,适合初学者练手。以下是具体实现思路和步骤。 …

    2025年12月16日
    000
  • Go语言:深入理解uint8到string的转换技巧

    本文将详细介绍在go语言中如何将`uint8`类型有效转换为字符串。当从字符串中索引单个字符(其类型为`uint8`)并尝试将其数值转换为字符串表示时,常见的错误是直接使用`strconv.itoa`。我们将阐明`uint8`和`int`之间的区别,并提供正确的类型转换方法,确保代码的健壮性和可读性…

    2025年12月16日
    000
  • Go语言中big.Int到指定进制字符串的转换方法与非导出函数探究

    本文深入探讨了在go语言中将`big.int`类型转换为自定义进制字符串的实践方法,特别是如何避免标准库`base32`包的额外格式。针对用户希望访问`math/big`包中非导出函数`nat.string`的需求,文章阐明了go语言中非导出函数无法直接访问的限制,并提出通过`strconv.for…

    2025年12月16日
    000
  • Go 项目测试文件组织:子目录、递归执行与覆盖率实践

    本文深入探讨 go 语言项目中测试文件的组织策略,重点介绍如何在子目录中管理测试、如何使用 `go test ./…` 命令进行递归测试,并分析其对包内容访问权限的影响。此外,文章还详细阐述了 go 1.20 引入的集成测试覆盖率功能,以及 `package_test` 模式的应用,旨在…

    2025年12月16日
    000
  • Go语言在Windows环境下清空控制台的有效方法

    本教程详细介绍了go语言在windows操作系统中清空控制台的正确方法。通过利用`os/exec`包,结合windows命令解释器`cmd`及其`/c`参数执行`cls`命令,可以有效实现控制台屏幕的清空。文章提供了完整的代码示例和详细解释,帮助开发者在go程序中实现跨平台(侧重windows)的控…

    2025年12月16日
    000
  • Go语言并发编程:构建健壮的通道复用器

    本文深入探讨了go语言中通道复用器的实现,旨在将多个输入通道的数据合并到一个输出通道。文章首先剖析了初学者在实现过程中常遇到的闭包中循环变量捕获和并发共享状态管理(如计数器)的常见陷阱,并解释了这些问题如何导致非预期行为。随后,详细介绍了如何利用`sync.waitgroup`和正确的gorouti…

    2025年12月16日
    000
  • Go语言命名返回值:原理、用法与最佳实践

    go语言的命名返回值提供了一种声明函数返回变量的便捷方式,允许通过空 `return` 语句隐式返回这些变量的当前值,或通过显式 `return` 语句覆盖它们。这种机制得益于go在栈上分配参数和返回值的底层实现,使得命名返回值成为函数签名中预定义存储位置的自然表达。理解其工作原理有助于编写更清晰、…

    2025年12月16日
    000
  • Go语言中io.Writer接口的正确初始化与使用

    本文深入探讨了go语言中`io.writer`接口未初始化导致的运行时错误(nil指针解引用)问题。通过分析接口的本质及其与具体实现的关联,文章展示了如何正确地声明和初始化`io.writer`变量,并提供了使用`os.stdout`和`bytes.buffer`等具体类型进行初始化的示例,旨在帮助…

    2025年12月16日
    000
  • Go 语言命名返回值:用法、原理与最佳实践

    go 语言的命名返回值是一项强大特性,它允许在函数签名中声明返回变量,从而简化代码并提高可读性。本文深入探讨了命名返回变量的用法,包括其隐式和显式返回机制,并通过解释 go 函数参数和返回值在栈上的分配原理,揭示了其底层工作方式。我们将通过示例代码和汇编分析,确认其使用的合法性与高效性,并提供实践建…

    2025年12月16日
    000
  • Go并发模式:安全有效地合并多个通道

    本文深入探讨了go语言中如何安全高效地合并多个通道(channel)的数据流到一个单一通道。我们将分析并发编程中常见的陷阱,如循环变量的闭包捕获问题和共享状态的竞态条件,并详细介绍如何利用`sync.waitgroup`机制来优雅地管理并发goroutine的生命周期,从而构建一个健壮的通道复用器。…

    2025年12月16日
    000
  • Go 语言命名返回值:深入理解与最佳实践

    go 语言的命名返回值提供了一种简洁的方式来声明和管理函数返回结果。它们不仅可以避免重复声明,还允许使用裸 return 语句隐式返回已命名的变量。这种机制通过在函数调用栈上预留空间实现,确保了代码的清晰性和效率,并且在go标准库中被广泛应用,是一种完全推荐的编程实践。 在 Go 语言中,函数可以返…

    2025年12月16日
    000
  • 深入理解Go语言中io.Writer接口的nil值与运行时错误

    在Go语言中,未初始化的`io.Writer`接口变量默认为`nil`,当尝试对其执行写入操作时,会导致“panic: runtime error: invalid memory address or nil pointer dereference”运行时错误。本文将详细解释这一现象的原因,并通过示…

    2025年12月16日
    000
  • Golang如何使用Kubernetes ConfigMap管理配置_Golang Kubernetes ConfigMap管理实践详解

    使用ConfigMap实现Golang应用配置管理,通过环境变量或文件挂载方式解耦配置,结合fsnotify监听实现热更新,提升应用灵活性与可维护性。 在 Kubernetes 环境中,应用配置与代码分离是最佳实践之一。Golang 作为云原生生态中的主流语言,天然适合与 Kubernetes 集成…

    2025年12月16日
    000
  • 如何在Golang中开发小型社交应用

    答案:Golang适合开发小型社交应用,其高性能和并发支持便于实现用户注册登录、发帖、关注系统和Feed流等核心功能。采用Gin或Echo框架,结合PostgreSQL与Redis,使用JWT鉴权和GORM操作数据库,通过分层架构设计提升可维护性,并以bcrypt加密密码、Redis缓存Feed优化…

    2025年12月16日
    000
  • Golang如何处理并发goroutine中的错误

    使用通道传递错误是Go并发中处理goroutine错误的核心方法,通过创建error类型通道让worker发送错误,主协程接收并处理。示例中doWork函数模拟出错,worker通过errCh发送错误,主函数读取并记录。采用缓冲通道可避免发送阻塞,尤其在多个worker场景下,主程序可等待所有完成后…

    2025年12月16日
    000
  • 如何在Golang中使用encoding/json处理JSON数据_Golang encoding/json JSON解析方法汇总

    Go的encoding/json库通过json.Marshal和Unmarshal实现结构体与JSON互转,配合struct标签可自定义字段名、忽略空值或私有字段;2. 使用map[string]interface{}和类型断言处理动态JSON,注意数字默认解析为float64;3. 对大文件采用j…

    2025年12月16日
    000
  • Go语言并发与锁机制的测试策略与最佳实践

    本文深入探讨了go语言中并发与锁机制测试的挑战与有效策略。它强调了传统日志驱动测试的局限性,并推荐利用go的`testing`包、`sync.waitgroup`和通道(channels)来自动化测试并发操作的顺序和阻塞行为。文章还进一步倡导采用go的csp(communicating sequen…

    2025年12月16日
    000
  • Go cgo在ARM平台上编译C标准库头文件问题解析与解决

    本文旨在解决go语言项目在arm架构(如树莓派)上使用cgo编译时,因找不到c标准库头文件(如`math.h`)而导致的构建失败问题。核心在于理解cgo的编译机制,并正确配置`// #cgo cflags`指令以指定c编译器头文件搜索路径,以及使用`// #cgo ldflags`链接必要的c库,避…

    2025年12月16日
    000

发表回复

登录后才能评论
关注微信