📚 浅拷贝 vs 深拷贝:前端面试解析
TIP
这是一份完整的浅拷贝与深拷贝面试指南
🔍 常见问题
1. 什么是浅拷贝和深拷贝?
拷贝类型 | 定义 | 特点 |
---|---|---|
浅拷贝 | 只复制第一层属性 | ✅ 性能好 ❌ 引用类型共享 |
深拷贝 | 递归复制所有层级 | ✅ 完全独立 ❌ 性能开销大 |
2. 实现浅拷贝的方法
javascript
// 方法一:基础循环实现
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;
}
// 方法二:Object.keys实现
const shallowCopy2 = obj => {
if (typeof obj !== 'object' || obj === null) return obj;
const newObj = Array.isArray(obj) ? [] : {};
Object.keys(obj).forEach(key => {
newObj[key] = obj[key];
});
return newObj;
}
// 测试用例
const obj = {
name: '浅拷贝',
info: { age: 18 }
};
const copyObj = shallowCopy(obj);
obj.info.age = 20;
console.log(copyObj.info.age); // 20 - 说明是浅拷贝
🎯 深拷贝实现
手写深拷贝
从简单到复杂的实现
javascript
// 基础版本 - 只处理基本类型和对象/数组
function simpleDeepCopy(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] = simpleDeepCopy(obj[key]);
}
}
return newObj;
}
JSON 方法的局限性
- [ ] 无法处理循环引用
- [ ] 无法复制函数
- [ ] 无法处理
undefined
和Symbol
- [ ] 特殊对象处理失败
Date
RegExp
Error
- [ ] 丢失原型链
深拷贝难点
mindmap root((深拷贝难点)) 循环引用 WeakMap记录 防止死循环 特殊对象 Date RegExp Map/Set 原型链 getPrototypeOf create继承 属性类型 Symbol键 不可枚举属性
完整的深拷贝实现
生产级别的深拷贝实现
javascript
/**
* @typedef {Object} Options
* @property {WeakMap} [hash] - 用于处理循环引用的 WeakMap
*/
/**
* 完整的深拷贝实现
* @param {*} obj - 要拷贝的对象
* @param {Options} [options] - 配置选项
* @returns {*} 深拷贝后的对象
*/
function completeDeepCopy(obj, hash = new WeakMap()) {
// 基础类型直接返回
if (obj === null || typeof obj !== 'object') return obj;
// 处理特殊对象
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
// 检测循环引用
if (hash.has(obj)) return hash.get(obj);
// 获取对象所有属性描述符
const allDesc = Object.getOwnPropertyDescriptors(obj);
// 创建一个新对象,并继承原型链
const clone = Object.create(Object.getPrototypeOf(obj), allDesc);
// 记录已拷贝过的对象
hash.set(obj, clone);
// 递归拷贝所有属性
for (let key of Reflect.ownKeys(obj)) {
clone[key] = completeDeepCopy(obj[key], hash);
}
return clone;
}
📝 面试答题要点
核心要点
区分概念
- [x] 浅拷贝与深拷贝的本质区别
- [x] 内存模型的差异
实现方法
- [x] 浅拷贝的多种实现
- [x] 深拷贝的不同方案
深拷贝难点
graph TD A[深拷贝难点] --> B[循环引用] A --> C[特殊对象] A --> D[原型链] A --> E[描述符] B --> F[WeakMap解决] C --> G[instanceof判断] D --> H[Object.create] E --> I[getOwnPropertyDescriptors]
🚀 进阶知识点
WeakMap 的作用
IMPORTANT
WeakMap 在深拷贝中扮演着关键角色
- 存储对象映射关系
- 防止内存泄漏javascript
const hash = new WeakMap(); // WeakMap 的键是弱引用,不会阻止垃圾回收
Reflect.ownKeys 的优势
javascript
const obj = {
[Symbol('key')]: 'value',
normal: 'normal'
};
// ✅ 获取所有类型的键
console.log(Reflect.ownKeys(obj));
// 输出: ['normal', Symbol(key)]
属性描述符示例
javascript
const obj = {
get value() { return this._value; },
set value(val) { this._value = val; }
};
const desc = Object.getOwnPropertyDescriptor(obj, 'value');
console.log(desc);
/* 输出:
{
get: [Function: get value],
set: [Function: set value],
enumerable: true,
configurable: true
}
*/