1. 浅拷贝 vs 深拷贝
-
浅拷贝:
- 只复制对象的第一层属性。如果属性值是基本类型(如数字、字符串),则复制值;如果属性值是引用类型(如对象、数组),则复制引用地址,修改新对象的引用类型属性会影响原对象。
- 特点:只复制一层,嵌套对象仍共享引用。
-
深拷贝:
- 完全复制对象及其所有嵌套层级的属性,生成独立的新对象。修改新对象不会影响原对象。
- 特点:递归复制所有层级,内存占用较高。
2. 浅拷贝的实现方法
以下是几种常见的浅拷贝实现方法:
方法1:Object.assign()
- 复制对象的第一层属性。
const obj = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, obj);
// 测试
shallowCopy.a = 10; // 不影响原对象
shallowCopy.b.c = 20; // 影响原对象的b.c
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(shallowCopy); // { a: 10, b: { c: 20 } }
方法2:展开运算符(…)
- 适用于对象和数组,复制第一层。
const arr = [1, [2, 3]];
const shallowCopy = [...arr];
// 测试
shallowCopy[0] = 10; // 不影响原数组
shallowCopy[1][0] = 20; // 影响原数组的[2, 3]
console.log(arr); // [1, [20, 3]]
console.log(shallowCopy); // [10, [20, 3]]
方法3:Array.slice()
- 仅适用于数组,复制第一层。
const arr = [1, [2, 3]];
const shallowCopy = arr.slice();
// 测试
shallowCopy[0] = 10; // 不影响原数组
shallowCopy[1][0] = 20; // 影响原数组
console.log(arr); // [1, [20, 3]]
console.log(shallowCopy); // [10, [20, 3]]
3. 深拷贝的实现方法
深拷贝需要递归处理嵌套对象,以下是几种实现方法:
方法1:JSON.parse(JSON.stringify())
- 简单但有局限性(无法处理函数、Date、RegExp、undefined等)。
const obj = { a: 1, b: { c: 2 }, d: [1, 2] };
const deepCopy = JSON.parse(JSON.stringify(obj));
// 测试
deepCopy.b.c = 20;
deepCopy.d[0] = 10;
console.log(obj); // { a: 1, b: { c: 2 }, d: [1, 2] }
console.log(deepCopy); // { a: 1, b: { c: 20 }, d: [10, 2] }
局限性:
- 无法复制函数:
{ fn: () => {} }->{ fn: null } - Date对象变为字符串:
{ date: new Date() }->{ date: "2025-11-06T..." } - 忽略undefined和Symbol。
方法2:递归实现深拷贝
- 手动实现,处理复杂数据类型。
function deepCopy(obj, cache = new WeakMap()) {
// 处理基本类型和null
if (obj === null || typeof obj !== 'object') return obj;
// 处理循环引用
if (cache.has(obj)) return cache.get(obj);
// 处理数组
if (Array.isArray(obj)) {
const copy = [];
cache.set(obj, copy);
obj.forEach((item, i) => {
copy[i] = deepCopy(item, cache);
});
return copy;
}
// 处理对象
const copy = {};
cache.set(obj, copy);
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], cache);
});
return copy;
}
// 测试
const obj = { a: 1, b: { c: 2 }, d: [1, 2] };
obj.circular = obj; // 循环引用
const copy = deepCopy(obj);
copy.b.c = 20;
copy.d[0] = 10;
console.log(obj); // { a: 1, b: { c: 2 }, d: [1, 2], circular: [Circular] }
console.log(copy); // { a: 1, b: { c: 20 }, d: [10, 2], circular: [Circular] }
特点:
- 支持循环引用(通过WeakMap)。
- 可扩展以处理特殊类型(如Date、RegExp)。
方法3:结构化克隆(structuredClone)
- 现代浏览器支持的原生API,处理大多数复杂类型。
const obj = { a: 1, b: { c: 2 }, d: new Date() };
const deepCopy = structuredClone(obj);
// 测试
deepCopy.b.c = 20;
console.log(obj); // { a: 1, b: { c: 2 }, d: Date }
console.log(deepCopy); // { a: 1, b: { c: 20 }, d: Date }
局限性:
- 浏览器环境独有,Node.js需18.0+版本。
- 不支持函数和某些特殊对象。
方法4:第三方库(如Lodash)
- 使用
_.cloneDeep实现深拷贝。
const _ = require('lodash');
const obj = { a: 1, b: { c: 2 }, fn: () => {} };
const deepCopy = _.cloneDeep(obj);
// 测试
deepCopy.b.c = 20;
console.log(obj); // { a: 1, b: { c: 2 }, fn: [Function] }
console.log(deepCopy); // { a: 1, b: { c: 20 }, fn: [Function] }
4. 深拷贝与浅拷贝的示例场景
浅拷贝场景
- 场景1:简单对象复制:
- 复制表单数据,仅修改第一层属性(如
{ name: "Alice", age: 25 })。 - 方法:
Object.assign()或...。
- 复制表单数据,仅修改第一层属性(如
- 场景2:数组操作:
- 复制数组进行排序或过滤,但不修改嵌套对象。
- 方法:
[...arr]或arr.slice()。
- 场景3:React状态更新:
- 更新React state时,复制state对象的第一层,避免直接修改原state。
- 示例:
setState({ ...state, count: state.count + 1 })。
深拷贝场景
- 场景1:复杂对象修改:
- 复制一个嵌套对象(如配置对象),需要独立修改嵌套属性。
- 方法:
deepCopy或structuredClone。
- 场景2:状态管理:
- 在Redux或Vuex中,复制store状态以避免直接修改,确保不可变性。
- 方法:
_.cloneDeep或自定义深拷贝。
- 场景3:数据备份:
- 保存初始数据副本,允许后续修改而不影响原始数据(如编辑器的“撤销”功能)。
- 方法:
JSON.parse(JSON.stringify())(简单场景)或deepCopy。
5. 注意事项与面试要点
- 浅拷贝局限:
- 只适合第一层为基本类型的对象。
- 常用于性能要求高的场景,因开销小。
- 深拷贝挑战:
- 处理循环引用、特殊类型(如Date、RegExp)。
- 性能开销较大,需权衡使用场景。
- 面试常见问题:
- Q:深拷贝和浅拷贝的区别?
A:浅拷贝只复制第一层,嵌套对象共享引用;深拷贝递归复制所有层级,新对象完全独立。 - Q:如何处理循环引用?
A:使用WeakMap记录已复制对象,避免无限递归。 - Q:JSON.stringify的局限性?
A:无法处理函数、undefined、Symbol、循环引用,Date对象转为字符串。
- Q:深拷贝和浅拷贝的区别?
3万+





