电脑学堂
第二套高阶模板 · 更大气的阅读体验

JavaScript深拷贝与浅拷贝:别再搞混了(实用技巧版)

发布时间:2025-12-14 17:49:08 阅读:8 次

ref="/tag/137/" style="color:#3D6345;font-weight:bold;">JavaScript的时候,经常会遇到要复制一个对象的情况。比如你做了一个用户表单,填完信息想存个副本,这时候直接用等号赋值,改副本的时候原数据也变了——问题就出在你用了浅拷贝。

赋值不等于复制

很多人一开始都会犯这个错误:

const user = { name: '张三', info: { age: 25 } };
const copy = user;
copy.name = '李四';
console.log(user.name); // 输出 '李四'

看到没?你改的是copy,user也跟着变了。因为这只是把引用地址传过去了,两个变量指向同一个对象,这根本不是复制。

浅拷贝:只复制一层

真正的拷贝得另想办法。浅拷贝能做到第一层属性的独立,但嵌套的对象还是共用的。

const user = { name: '张三', info: { age: 25 } };
const copy = Object.assign({}, user);
// 或者用解构
// const copy = { ...user };

copy.info.age = 30;
console.log(user.info.age); // 输出 30

name能分开改,但info是对象,它内部的age还是共享的。这就是浅拷贝的坑:看起来分开了,其实里子还连着。

深拷贝:彻底断开联系

要想完全独立,就得深拷贝。最简单的方法是序列化再解析,适合纯数据对象:

const user = { name: '张三', info: { age: 25 } };
const copy = JSON.parse(JSON.stringify(user));

copy.info.age = 30;
console.log(user.info.age); // 输出 25,终于不影响了

但这招有局限。函数、undefined、Symbol会被丢掉,Date类型会变成字符串,循环引用还会报错。真要用在复杂场景,得上专门的库,比如 lodash 的 cloneDeep。

实际场景中的选择

你在写一个购物车功能,用户点“保存当前状态”,你得把当前商品列表存下来。如果只是浅拷贝,后面修改商品数量时,历史记录也会变,用户一刷新发现之前的数据不对劲,这就糟了。

但如果数据结构简单,比如只有商品ID和数量,没有嵌套配置,那用展开运算符 {...cart} 完全够用,性能还好。

自己实现一个简易深拷贝

了解原理也有必要。下面是个递归实现的基本版本:

function deepClone(obj) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof Array) {
    return obj.map(item => deepClone(item));
  }
  const cloned = {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloned[key] = deepClone(obj[key]);
    }
  }
  return cloned;
}

这个函数能处理基本的嵌套对象和数组,虽然不包括Map、Set这些特殊类型,但在多数业务逻辑里已经够用了。

深拷贝和浅拷贝不是选哪个高级的问题,而是看你要不要彻底切断关联。搞清楚它们的区别,才能避免莫名其妙的数据污染问题。