Skip to content

📚 浅拷贝 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 方法的局限性

  • [ ] 无法处理循环引用
  • [ ] 无法复制函数
  • [ ] 无法处理 undefinedSymbol
  • [ ] 特殊对象处理失败
    • 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;
}

📝 面试答题要点

核心要点

  1. 区分概念

    • [x] 浅拷贝与深拷贝的本质区别
    • [x] 内存模型的差异
  2. 实现方法

    • [x] 浅拷贝的多种实现
    • [x] 深拷贝的不同方案
  3. 深拷贝难点

    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 在深拷贝中扮演着关键角色

  1. 存储对象映射关系
  2. 防止内存泄漏
    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
}
*/

📚 相关资源