Object.assign({}, obj) Object.assign({}, obj) Object.assign({}, obj)
面试题库 词条目录
前端Wiki/面试题库/JavaScript/手写代码题

深浅拷贝与实现

浅拷贝只复制一层:对象属性值是引用类型时仍与原对象共享同一份。原生浅拷贝是 Object.assign({}, obj) 或展开 {...obj}。深拷贝递归复制所有层级,最实用的答案是 structuredClone(obj)(现代浏览器内置,支持循环引用,不拷贝函数);旧方案 JSON.parse(JSON.stringify(obj)) 不支持函数、undefined、循环引用。

面试真正考的是手写深拷贝:递归 + WeakMap 记录已拷贝对象(解决循环引用)+ 特殊对象处理(数组、Date、RegExp、Map/Set)。用 WeakMap 而非 Map:键是弱引用,拷贝完成后可被垃圾回收,不长期占用内存。

  • 浅拷贝:Object.assign / 展开运算符,只复制一层
  • JSON 序列化法不支持函数 / undefined / 循环引用,structuredClone 是现代答案
  • 手写要点:递归 + WeakMap 破循环引用 + Date/RegExp/Map/Set 特判
JS
// 浅拷贝
const a = { x: 1, sub: { y: 2 } };
const b = { ...a };
b.sub.y = 99;
console.log('浅拷贝共享引用:', a.sub.y === 99);     // true —— 原对象被改了

// 深拷贝:现代一行版
const c = structuredClone(a);
c.sub.y = 0;
console.log('structuredClone 后原对象:', a.sub.y);   // 2 —— 互不影响

// 手写深拷贝(面试标答:递归 + WeakMap + 特殊类型)
function deepClone(obj, hash = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;   // 原始值直接返回
  if (hash.has(obj)) return hash.get(obj);                   // 循环引用:返回已拷贝副本
  // 特殊对象
  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags);
  if (obj instanceof Map) {
    const m = new Map(); hash.set(obj, m);
    obj.forEach((v, k) => m.set(deepClone(k, hash), deepClone(v, hash)));
    return m;
  }
  if (obj instanceof Set) {
    const s = new Set(); hash.set(obj, s);
    obj.forEach(v => s.add(deepClone(v, hash)));
    return s;
  }
  // 普通对象 / 数组
  const clone = Array.isArray(obj) ? [] : {};
  hash.set(obj, clone);
  Object.keys(obj).forEach(k => { clone[k] = deepClone(obj[k], hash); });
  return clone;
}

// 测试循环引用
const o = { n: 1 };
o.self = o;
const o2 = deepClone(o);
console.log('循环引用通过:', o2.self === o2);        // true,且没有爆栈
本节要点深拷贝三要素:递归复制、WeakMap 破循环引用、Date/RegExp/Map/Set 特殊处理;实际项目用 structuredClone。
🔗 相关词条JavaScript原型链闭包