You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript如何正确复制对象?解决展开运算符浅拷贝问题及高效深克隆方法

为什么修改拷贝对象的数组会影响原对象?以及JS高效深克隆方法

这是前端开发里非常常见的浅拷贝陷阱,我来给你一步步拆解清楚:

问题根源:展开运算符是浅拷贝

你用{ ...obj1 }复制对象时,只会复制对象的第一层属性:

  • 对于a、b这类基本类型(字符串),是直接复制它们的值,所以obj2的a和obj1的a是完全独立的;
  • 但对于c这个数组(引用类型),展开运算符复制的是数组的内存引用——也就是说obj2的c和obj1的c指向的是同一个数组。所以你修改obj2.c[1]时,其实是在修改那个共享的数组,自然原对象obj1的c也会跟着变。

解决你的示例问题:手动拷贝引用类型属性

如果只是处理你这个简单的对象,只需要单独拷贝数组属性,让它生成新的引用:

let obj1 = { a:'hello', b:'hi', c: [1,2,3] }
let obj2 = { 
  ...obj1,
  c: [...obj1.c] // 这里用展开运算符复制数组,创建一个全新的数组
}
obj2.c[1] = 44

console.log(obj1.c) // [1,2,3],原数组不受影响
console.log(obj2.c) // [1,44,3],只有拷贝后的数组被修改

如果对象里还有更深层次的嵌套对象,那就要用到深克隆了。

JavaScript中最高效的深克隆方法

1. 优先使用原生API:structuredClone()

这是ES2022引入的原生深克隆方法,也是现在最推荐的方案:

  • 性能优秀,比大部分手写递归函数效率高;
  • 支持多种类型:普通对象、数组、Date、Map、Set、TypedArray,甚至能处理循环引用(比如对象里引用自身的情况);
  • 使用简单:
const obj1 = { a:'hello', b:'hi', c: [1,2,3], d: new Date() }
const deepClonedObj = structuredClone(obj1)

deepClonedObj.c[1] = 44
deepClonedObj.d.setDate(10)

console.log(obj1.c) // [1,2,3],原数组不变
console.log(obj1.d) // 原日期对象不受影响

唯一需要注意的是,它不支持克隆函数、Symbol属性,而且旧浏览器(比如IE)完全不支持,但现代浏览器(Chrome、Firefox、Edge等)都已经支持了。

2. 手写递归深克隆函数(兼容旧环境或特殊需求)

如果需要兼容旧环境,或者要处理函数这类structuredClone不支持的类型,可以自己写递归函数,记得处理循环引用避免死循环:

function deepClone(obj, cache = new WeakMap()) {
  // 基本类型直接返回
  if (obj === null || typeof obj !== 'object') return obj;
  
  // 处理特殊引用类型
  if (obj instanceof Date) return new Date(obj.getTime());
  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
  
  // 处理循环引用:如果已经克隆过这个对象,直接返回缓存的克隆结果
  if (cache.has(obj)) return cache.get(obj);
  
  // 创建数组或对象的克隆容器
  const clone = Array.isArray(obj) ? [] : {};
  // 缓存当前对象,避免循环引用
  cache.set(obj, clone);
  
  // 遍历并递归克隆所有属性
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] = deepClone(obj[key], cache);
    }
  }
  
  return clone;
}

这个函数可以处理大部分场景,包括循环引用,还能自定义处理特殊类型。

3. 不推荐:JSON.parse(JSON.stringify(obj))

虽然这个方法看起来简单,但有很多致命缺点:

  • 会丢失函数、undefined、Symbol类型的属性;
  • 日期对象会被转成字符串,无法保留日期类型;
  • 正则表达式会被转成空对象;
  • 无法处理循环引用,会直接报错;
    所以只适合处理非常简单的、没有特殊类型的对象,不推荐作为通用深克隆方案。

内容的提问来源于stack exchange,提问作者Gustavo Hiroki Nakagawa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:15:59