在 JavaScript 中,深浅拷贝是处理对象复制的重要概念。它们的核心区别在于对 引用类型数据 的处理方式,理解这一点对避免程序中的意外数据污染至关重要。
一、核心概念解析
1. 基本类型 vs 引用类型
-
基本类型:
Number
,String
,Boolean
,null
,undefined
,Symbol
,BigInt
直接存储在栈内存中,复制时创建独立副本。 -
引用类型:
Object
,Array
,Function
,Date
,RegExp
等
栈内存存储指针,堆内存存储实际数据,复制时默认只复制指针。
2. 赋值 vs 浅拷贝 vs 深拷贝
操作类型 | 特点 | 是否共享引用 |
---|---|---|
直接赋值 | 复制指针,完全共享数据 | ✅ |
浅拷贝 | 创建新对象,但嵌套引用类型仍共享 | 嵌套层级共享 ✅ |
深拷贝 | 完全独立的新对象,所有层级不共享 | ❌ |
二、浅拷贝实现方案
1. 手动实现
function shallowCopy(obj) {if (typeof obj !== 'object' || obj === null) return obj;const newObj = Array.isArray(obj) ? [] : {};for (let key in obj) {if (obj.hasOwnProperty(key)) {newObj[key] = obj[key]; // 仅复制第一层}}return newObj;
}
2. 内置方法
-
Object.assign()
const obj = { a: 1, b: { c: 2 } }; const copy = Object.assign({}, obj);
-
展开运算符
...
const arr = [1, { x: 2 }]; const arrCopy = [...arr];
3. 特性验证
const original = { a: 1, b: { c: 2 } };
const copy = shallowCopy(original);copy.b.c = 999; // 修改嵌套对象
console.log(original.b.c); // 999 → 数据被污染
三、深拷贝实现方案
1. JSON 序列化法
const deepCopyJSON = obj => JSON.parse(JSON.stringify(obj));
局限性:
- 无法处理
undefined
、Symbol
、函数 - 忽略原型链
- 不能解决循环引用
- 会破坏特殊对象(如
Date
变为字符串)
2. 递归实现(基础版)
function deepClone(source) {if (source === null || typeof source !== 'object') return source;const target = Array.isArray(source) ? [] : {};for (let key in source) {if (source.hasOwnProperty(key)) {target[key] = deepClone(source[key]); // 递归复制}}return target;
}
3. 增强版深拷贝
处理复杂场景:
function enhancedDeepClone(source, map = new WeakMap()) {// 处理循环引用if (map.has(source)) return map.get(source);// 处理特殊对象if (source instanceof Date) return new Date(source);if (source instanceof RegExp) return new RegExp(source);const target = Array.isArray(source) ? [] : {};map.set(source, target);// 处理Symbol作为keyconst symKeys = Object.getOwnPropertySymbols(source);symKeys.forEach(symKey => {target[symKey] = enhancedDeepClone(source[symKey], map);});for (let key in source) {if (source.hasOwnProperty(key)) {target[key] = enhancedDeepClone(source[key], map);}}return target;
}
4. 第三方库
- lodash 的
_.cloneDeep()
import _ from 'lodash'; const perfectCopy = _.cloneDeep(original);
四、性能与选择策略
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
浅拷贝 | 速度快、内存占用少 | 嵌套数据不安全 | 简单对象结构 |
JSON法 | 简单快捷 | 丢失特殊类型 | 无特殊类型的数据 |
递归深拷贝 | 完全控制 | 性能消耗大 | 复杂对象结构 |
第三方库 | 功能完善 | 增加依赖 | 生产环境推荐 |
五、应用场景示例
-
状态管理
Redux 的 reducer 中必须使用深拷贝保证状态不可变性:function reducer(state, action) {return {...state,user: _.cloneDeep(action.payload) // 确保状态独立}; }
-
数据快照
保存历史记录时需完全独立的数据副本:const gameState = { players: [{ score: 0 }], level: 1 }; const history = [deepClone(gameState)];
-
配置对象复用
修改配置模板不影响原始模板:const defaultConfig = { theme: 'dark', permissions: { read: true } }; const userConfig = deepClone(defaultConfig); userConfig.permissions.write = false;
六、常见误区
-
误以为
Object.assign()
是深拷贝
实际上它只能实现第一层深拷贝,嵌套对象仍是浅拷贝。 -
忽略循环引用问题
const obj = { a: 1 }; obj.self = obj; deepClone(obj); // 基础实现会栈溢出
-
处理特殊对象不当
直接克隆Date
对象会丢失方法:const date = new Date(); const badCopy = JSON.parse(JSON.stringify(date)); // 变成字符串
掌握深浅拷贝的底层原理和实现方式,能帮助开发者根据实际需求选择最优解决方案,在保证数据安全性的同时平衡性能消耗。对于日常开发,推荐优先使用成熟的工具库(如 lodash),但在需要精细控制时,理解手写实现原理至关重要。