彻底理解 JavaScript 的深浅拷贝

引言

在我写代码的这几年里,关于对象的拷贝可以说是老生常谈的问题。尤其是当我在项目里修改某个对象属性时,另一个变量也跟着变了的时候,我才意识到:自己其实并没有真正理解"浅拷贝"和"深拷贝"的区别。

这篇文章,我想从自己的思考和实践出发,完整整理一下我对 JavaScript 深浅拷贝的理解和实现方式。

一、为什么要拷贝?

在 JavaScript 中,对象、数组、函数等引用类型是通过引用地址存储的。

ini 复制代码
const obj1 = { name: 'Liu', age: 25 }
const obj2 = obj1

obj2.age = 30
console.log(obj1.age) // 30

这时候 obj1obj2 指向的是同一个内存地址,我改动一个,另一个就跟着变。

有些时候我只想复制出一份独立的数据,这就是"拷贝"存在的意义。


二、浅拷贝:复制第一层的引用

浅拷贝只会复制对象的第一层属性,更深层的对象依然是引用关系。

最常见的浅拷贝方式有几种:

1. Object.assign()

ini 复制代码
const obj = { name: 'Liu', info: { age: 25 } }
const copy = Object.assign({}, obj)
copy.info.age = 30

console.log(obj.info.age) // 30

虽然看起来 copy 是个新对象,但 info 还是共享的引用。

2. 展开运算符(...)

go 复制代码
const copy = { ...obj }

效果和 Object.assign() 一样,只拷贝第一层。


三、深拷贝:复制整个结构

深拷贝会递归复制对象的所有层级,让副本与原对象完全独立。

1. JSON 方法(最简单的)

ini 复制代码
const obj = { name: 'Liu', info: { age: 25 } }
const copy = JSON.parse(JSON.stringify(obj))

优点是简单直接,缺点也不少:

  • 无法拷贝 undefinedSymbolFunction
  • 会丢失原型链
  • 无法处理循环引用
  • 日期会被转成字符串

但对于普通数据结构,这种方式足够用。


四、更靠谱的深拷贝实现

在我自己写项目时,真正遇到复杂对象(比如含 DateMapSet、循环引用)的情况,JSON 已经不够用了。

我后来总结出几种更稳定的做法:

1. MessageChannel 异步克隆

有时候我不想阻塞主线程,就用这种异步克隆技巧:

javascript 复制代码
function cloneAsync(value) {
  return new Promise(resolve => {
    const { port1, port2 } = new MessageChannel()
    port1.onmessage = e => resolve(e.data)
    port2.postMessage(value)
  })
}

const clone = await cloneAsync(obj)

它利用浏览器的结构化克隆机制完成异步复制,非常稳定,也能处理循环引用。

2. 手写递归版

javascript 复制代码
function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj
  if (hash.has(obj)) return hash.get(obj)

  const result = Array.isArray(obj) ? [] : {}
  hash.set(obj, result)

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      result[key] = deepClone(obj[key], hash)
    }
  }

  return result
}

这个版本支持循环引用,不会陷入死循环,是我自己项目里常用的一版。

当然,如果要支持 Map、Set、Symbol 等类型,还可以进一步扩展。

3. structuredClone

go 复制代码
const copy = structuredClone(obj)

它能自动处理:

  • 循环引用
  • Date、RegExp、Map、Set、TypedArray
  • 深层结构

就是函数、原型链还是不能保留,但在大多数前端场景下已经非常完美。

4. 三方工具

如果项目中已有该依赖,推荐使用,否则建议手写,或其他API。\

4.1. lodash

css 复制代码
const obj = { a: 1 }
_.cloneDeep(obj)

4.2. jquery

javascript 复制代码
// 浅拷贝
const dest = $.extend({}, src) // 等价于 Object.assign({}, src)
// 深拷贝
const deep = $.extend(true, {}, src)
相关推荐
LaughingZhu4 小时前
Product Hunt 每日热榜 | 2026-05-21
前端·人工智能·经验分享·chatgpt·html
怕浪猫4 小时前
Electron 开发实战(一):从零入门核心基础与环境搭建
前端·electron·ai编程
小鹏linux5 小时前
Ubuntu 22.04 部署开源免费具有精美现代web页面的Casdoor账号管理系统
linux·前端·ubuntu·开源·堡垒机
前端若水6 小时前
会话管理:创建、切换、删除对话历史
前端·人工智能·python·react.js
Bigger6 小时前
mini-cc:一个轻量级 AI 编程助手的诞生
前端·ai编程·claude
涵涵(互关)6 小时前
Naive-ui树型选择器只显示根节点
前端·ui·vue
BY组态6 小时前
Ricon组态系统最佳实践:从零开始构建物联网监控平台
前端·物联网·iot·web组态·组态
BY组态7 小时前
Ricon组态系统vs传统组态软件:为什么选择新一代Web组态平台
前端·物联网·iot·web组态·组态
SoaringHeart7 小时前
Flutter进阶:OverlayEntry 插入图层管理器 NOverlayZIndexManager
前端·flutter
放下华子我只抽RuiKe57 小时前
React 从入门到生产(四):自定义 Hook
前端·javascript·人工智能·深度学习·react.js·自然语言处理·前端框架