- 浅拷贝 (Shallow Copy)
只复制对象的表层属性,如果属性值是引用类型(如对象、数组),则拷贝的是引用地址,原对象和拷贝对象会共享这些引用类型的数据。
常见的浅拷贝方法:
// 1. Object.assign()
const obj1 = { a: 1, b: { c: 2 } };
const copy1 = Object.assign({}, obj1);
// 2. 扩展运算符 ...
const obj2 = { a: 1, b: { c: 2 } };
const copy2 = { ...obj2 };
// 3. 数组浅拷贝
const arr1 = [1, [2, 3]];
const copy3 = arr1.slice();
const copy4 = [...arr1];
- 深拷贝 (Deep Copy)
完全复制对象的所有层级,包括嵌套的引用类型,原对象和拷贝对象相互独立,修改其中一个不会影响另一个。
常见的深拷贝方法:
// 方法1: JSON序列化(简单场景适用,有局限性)
function deepCopyByJSON(obj) {
return JSON.parse(JSON.stringify(obj));
}
// 局限性:不能拷贝函数、正则、循环引用等
// 方法2: 递归实现深拷贝(更完善)
function deepCopy(obj, hash = new WeakMap()) {
// 处理null和基本类型
if (obj === null || typeof obj !== 'object') return obj;
// 处理日期
if (obj instanceof Date) return new Date(obj);
// 处理正则
if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
// 处理循环引用
if (hash.has(obj)) return hash.get(obj);
// 创建新对象/数组
let cloneObj = Array.isArray(obj) ? [] : {};
hash.set(obj, cloneObj);
// 递归拷贝属性
Reflect.ownKeys(obj).forEach(key => {
cloneObj[key] = deepCopy(obj[key], hash);
});
return cloneObj;
}
// 使用示例
const original = {
name: "test",
age: 20,
info: {
address: "city",
hobbies: ["reading", "coding"]
},
date: new Date(),
regex: /hello/g
};
const deepClone = deepCopy(original);
- 深浅拷贝的主要区别
浅拷贝:只拷贝第一层,嵌套对象共享引用
深拷贝:拷贝所有层级,嵌套对象完全独立 - 使用场景建议
简单数据结构且无嵌套引用类型:可以使用浅拷贝
复杂数据结构或有嵌套引用类型:需要使用深拷贝
注意循环引用问题:自定义递归深拷贝时需处理
应用场景:
- 浅拷贝的典型用场景
浅拷贝适用于 单层数据结构 或 不需要完全隔离嵌套对象 的场景,因为它性能更好(无需递归处理深层结构)。
(1)简单数据的复制与修改
当对象 / 数组仅包含基本类型(字符串、数字、布尔等),没有嵌套引用类型时,浅拷贝足够安全。
// 用户信息(无嵌套对象)
const user = { name: "Alice", age: 25, isActive: true };
// 浅拷贝:修改副本不影响原对象
const updatedUser = { ...user };
updatedUser.age = 26;
console.log(user.age); // 25(原对象不变)
(2)状态管理中的部分更新
在 React/Vue 等框架中,更新状态时常用浅拷贝创建新对象,触发视图更新(无需深拷贝整个状态树)。
// React 状态更新(仅修改部分属性)
const [form, setForm] = useState({ username: "", password: "" });
// 浅拷贝现有状态,只更新需要修改的字段
setForm({ ...form, username: "newName" });
(3)数组的简单处理
对数组进行切片、过滤等操作时,浅拷贝可快速创建新数组(适用于数组元素为基本类型的情况)。
const arr = [1, 2, 3, 4];
const filtered = arr.filter(item => item > 2); // 浅拷贝新数组
const sliced = arr.slice(1, 3); // 浅拷贝子数组
- 深拷贝的典型用场景
深拷贝适用于 包含嵌套引用类型 且 需要完全隔离原数据与副本 的场景,避免修改副本时影响原数据。
(1)复杂数据的备份与编辑
当数据包含多层嵌套对象 / 数组(如表单配置、树形结构),必须用深拷贝确保原数据不被意外修改。
// 嵌套结构的配置数据
const config = {
theme: "light",
settings: {
notifications: true,
layout: { columns: 2 }
}
};
// 深拷贝:完全隔离原数据
const editedConfig = deepCopy(config);
editedConfig.settings.layout.columns = 3;
console.log(config.settings.layout.columns); // 2(原数据不变)
(2)避免循环引用导致的问题
在处理可能包含循环引用的数据(如 DOM 元素引用、双向关联对象)时,需用支持循环引用的深拷贝方法。
const obj = { name: "A" };
obj.self = obj; // 循环引用(obj 引用自身)
// 用支持循环引用的深拷贝函数
const copy = deepCopy(obj); // 不会报错,且能正确复制
(3)缓存与数据快照
需要保存数据的历史状态(如撤销 / 重做功能)时,深拷贝可创建独立的快照,避免历史记录被后续操作污染。
// 保存编辑器历史记录
const history = [];
let currentState = { content: "hello", cursor: { x: 5, y: 10 } };
// 深拷贝当前状态到历史记录
history.push(deepCopy(currentState));
// 修改当前状态后,历史记录不受影响
currentState.content = "world";
console.log(history[0].content); // "hello"
(4)函数参数的安全处理
当函数需要修改传入的对象参数,又不想影响外部原始数据时,深拷贝可隔离参数与原对象。
function processData(data) {
// 深拷贝参数,避免修改外部数据
const copied = deepCopy(data);
copied.details = "processed";
return copied;
}
const original = { id: 1 };
const result = processData(original);
console.log(original.details); // undefined(原对象未被修改)
- 如何选择?
优先浅拷贝:单层数据、性能敏感场景、仅需部分更新数据。
必须深拷贝:嵌套数据、需要完全隔离的场景(如备份、历史记录)、包含循环引用。