JavaScript中浅拷贝只复制对象属性值或引用地址,新旧对象共享内存;深拷贝则创建完全独立副本,修改互不影响。常用方法包括JSON法(简单但限制多)、structuredClone(现代标准,支持循环引用)、手写递归和Lodash.cloneDeep(生产推荐)。

JavaScript里没有真正意义上的“传引用”,所有参数传递都是值传递——但这个“值”对引用类型来说,是指向堆内存对象的地址副本。所以判断关键不在“传什么”,而在变量类型和后续操作是否修改了同一块堆内存。
怎么判断是“传值效果”还是“传引用效果”
看变量类型和操作行为,不是看语法本身:
- 原始类型(number、string、boolean、null、undefined、symbol、bigint):赋值或传参时,复制的是真实值本身。修改副本不影响原变量。
-
引用类型(object、array、function、date、regexp 等):赋值或传参时,复制的是内存地址(即引用的值)。如果只改属性(如
obj.a = 10),原对象同步变化;如果重新赋值(如obj = {}),只是让新变量指向别处,不影响原变量。 - 字符串是个特例:虽属原始类型,但引擎内部可能复用内存;不过因不可变性,任何修改都会生成新字符串,所以行为上等同于传值。
深拷贝对象的常用方式
目标是让新对象与原对象完全独立,互不干扰,尤其要处理嵌套对象、数组、循环引用等。
-
JSON 方法(简单场景可用):
JSON.parse(JSON.stringify(obj))。快且简洁,但会丢弃函数、undefined、Symbol、RegExp、Date、BigInt,且无法处理循环引用。 -
结构化克隆(现代推荐):
structuredClone(obj)(Chrome 98+、Firefox 98+、Node.js 17.4+ 支持)。能正确处理 Map、Set、Date、RegExp、ArrayBuffer、甚至循环引用,是目前最可靠的标准方案。 - 递归手写(学习/兼容旧环境):先判断类型,对对象/数组递归复制;用 WeakMap 记录已拷贝对象,防止循环引用死循环;单独处理 Date、RegExp、Map、Set 等特殊类型。
-
第三方库(生产环境稳妥选择):Lodash 的
_.cloneDeep()经过大量边界测试,支持各种边缘情况,适合复杂业务。
什么时候该用深拷贝
不是所有场景都需要深拷贝,滥用会影响性能:
立即学习“Java免费学习笔记(深入)”;
- 函数参数中需隔离外部状态(如配置对象被内部修改)
- React/Vue 中更新不可变数据(避免直接修改 state 或 props)
- 表单重置、历史快照、撤销操作等需要保留原始副本
- 跨 iframe 或 Worker 传递数据前做安全隔离
真正要把握的是:原始类型天然“隔离”,引用类型靠操作方式决定是否“联动”。深拷贝不是万能解药,而是为特定需求服务的明确手段。


















