浅拷贝和深拷贝
一、定义
-
浅拷贝(Shallow Copy)
-
拷贝的是对象的第一层属性值:
-
如果属性是基本类型,直接拷贝值;
-
如果属性是引用类型(如对象、数组、函数),拷贝的是引用地址,并不会复制内容。
-
-
结果:原始对象和新对象共享引用类型属性。
-
-
深拷贝(Deep Copy)
-
递归地拷贝对象的所有层级属性:
-
不论是基本类型还是引用类型,
-
最终生成的对象是完全独立的副本,与原对象没有任何共享。
-
-
结果:原始对象和新对象完全独立,互不影响。
-
二、实现方式
1. 浅拷贝实现方式
方法一:Object.assign()
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, obj);
方法二:展开运算符(spread syntax)
const shallowCopy = { ...obj };
方法三:数组用 slice() 或 concat()
const arr = [1, 2, 3];
const newArr = arr.slice(); // 或 arr.concat();
注意:这些方法只复制一层,嵌套对象仍然是引用。
2. 深拷贝实现方式
方法一:JSON.parse(JSON.stringify(obj))【简单、常见】
const deepCopy = JSON.parse(JSON.stringify(obj));
-
优点:简单直接。
-
缺点:
-
会忽略 undefined、function、symbol。
-
无法处理 Date、RegExp、Map、Set 等特殊类型。
-
会丢失原型链。
-
方法二:递归实现
function deepClone(obj, map = new WeakMap()) {// 原始类型直接返回(包括 BigInt、Symbol、null、undefined、string 等)if (obj === null || typeof obj !== 'object') return obj;// 循环引用处理if (map.has(obj)) return map.get(obj);// 特殊类型处理if (obj instanceof Date) return new Date(obj.getTime());if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);if (obj instanceof Error) {const copy = new obj.constructor(obj.message);copy.stack = obj.stack;return copy;}// 函数不拷贝,直接返回原始引用(你也可以选择抛错或深克隆 Function)if (typeof obj === 'function') return obj;// 保留原型const result = Array.isArray(obj)? []: Object.create(Object.getPrototypeOf(obj));// 保存到 WeakMap,处理循环引用map.set(obj, result);// 拷贝所有属性(包括 Symbol 和不可枚举)Reflect.ownKeys(obj).forEach((key) => {const desc = Object.getOwnPropertyDescriptor(obj, key);if (desc && 'value' in desc) {desc.value = deepClone(obj[key], map);}Object.defineProperty(result, key, desc);});return result;
}
方法三:使用 Lodash 的 cloneDeep
npm install lodash
import cloneDeep from 'lodash/cloneDeep';const deepCopy = cloneDeep(obj);
- 最稳定,支持各种边界情况。
三、实际应用场景
浅拷贝使用场景
- 只操作对象的第一层结构时:
const state = { count: 1 };
const newState = { ...state, count: 2 };
- React/Vue中设置状态或props更新时,常用于不可变数据操作。
深拷贝使用场景
-
需要完全隔离副本,防止引用污染:
-
Redux 状态管理中做 immutable 数据更新;
-
配置数据模板复制;
-
表单的“还原为初始值”操作;
-
克隆嵌套数据结构(如嵌套对象或数组);
-
四、图示理解
const original = {name: "小明",info: { age: 26 }
};const shallow = { ...original };
shallow.info.age = 30;console.log(original.info.age); // 💥 30(引用被共享)const deep = JSON.parse(JSON.stringify(original));
deep.info.age = 40;console.log(original.info.age); // ✅ 30(深拷贝后独立)
五、总结对比表格
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
是否递归 | 否 | 是 |
引用类型共享 | 是(只拷贝引用) | 否(完全复制) |
实现难度 | 简单 | 相对复杂 |
性能 | 快 | 慢(尤其大数据结构) |
适用场景 | 扁平数据结构 | 嵌套结构、需要独立副本时 |