Skip to content
On This Page

深拷贝和浅拷贝

1. 为什么需要拷贝

JavaScript 中的对象(包括数组、函数等)是引用类型。直接使用赋值操作(let obj2 = obj1)并不会复制对象本身,而是复制了一个指向同一内存地址的引用。修改 obj2 的属性会同时影响到 obj1

javascript
const original = { a: 1, b: { c: 2 } };
const copy = original;
copy.a = 100;
console.log(original.a); // 100 —— 原对象被改变了!

为了避免这种“意外联动”,我们需要创建对象的拷贝(副本)。根据拷贝的深度不同,分为浅拷贝和深拷贝。

2. 浅拷贝(Shallow Copy)

2.1 定义

浅拷贝创建一个新对象,这个对象拥有原对象属性值的一份精确副本

  • 如果属性是基本数据类型(number, string, boolean, null, undefined, symbol),拷贝的是值;
  • 如果属性是引用类型(对象、数组、函数等),拷贝的是内存地址(引用)。因此,修改新对象的引用类型属性会影响原对象。

2.2 常见的浅拷贝方法


方法1:展开运算符(Spread)...

javascript
const original = { a: 1, b: { c: 2 } };
const shallow = { ...original };
shallow.a = 100;          // 修改基本类型,不影响原对象
shallow.b.c = 200;        // 修改嵌套对象的属性,原对象也会改变
console.log(original.b.c); // 200

方法2:Object.assign()

javascript
const shallow = Object.assign({}, original);

方法3:数组浅拷贝

  • arr.slice()
  • arr.concat()
  • [...arr]
  • Array.from(arr)

3. 深拷贝(Deep Copy)

3.1 定义

深拷贝递归地拷贝对象的所有层级,新对象与原对象在内存中完全独立。修改新对象的任何属性都不会影响原对象。

3.2 实现深拷贝的方法


方法1:JSON.parse(JSON.stringify(obj))(最常用,但有局限性)

javascript
const original = { a: 1, b: { c: 2 } };
const deep = JSON.parse(JSON.stringify(original));
deep.b.c = 200;
console.log(original.b.c); // 2 —— 不受影响

局限性(面试常考):

  • 无法拷贝 undefinedfunctionSymbol(它们会被忽略或转为 null);
  • 无法拷贝循环引用的对象(会抛出错误);
  • 无法拷贝 DateRegExpMapSetBlob 等特殊对象(会变成普通对象或字符串);
  • 无法拷贝对象的原型链(__proto__ 丢失)。
javascript
const obj = {
  fn: function() {},    // 被忽略
  undef: undefined,     // 被忽略
  sym: Symbol('foo'),   // 被忽略
  date: new Date()      // 变成字符串
};
const cloned = JSON.parse(JSON.stringify(obj));
console.log(cloned); // { date: '2025-...' }

方法2:使用 structuredClone()(现代浏览器/Node 17+)

structuredClone 是浏览器原生提供的深拷贝 API,支持大多数内置类型(DateRegExpMapSetArrayBuffer 等),也支持循环引用。

不支持FunctionSymbolDOM 元素 等。

javascript
const original = { 
  map: new Map([['key', 'value']]), 
  date: new Date(),
  circle: null 
};
original.circle = original;   // 循环引用
const deep = structuredClone(original);
console.log(deep.map === original.map); // false

方法3:使用第三方库 lodash

优点:稳定、全面、性能好。缺点:引入额外体积。

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

方法4:递归实现(手写深拷贝)

javascript
function deepClone(obj, hash = new WeakMap()) {
  // 处理 null 或非对象
  if (obj === null || typeof obj !== 'object') return obj;
  
  // 处理循环引用
  if (hash.has(obj)) return hash.get(obj);
  
  // 处理日期、正则
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj);
  
  // 处理数组或对象
  const cloneObj = Array.isArray(obj) ? [] : {};
  hash.set(obj, cloneObj);
  
  // 遍历自身及继承的可枚举属性(可根据需求调整)
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      cloneObj[key] = deepClone(obj[key], hash);
    }
  }
  return cloneObj;
}