12、✅ 手写浅拷贝与 Object.assign 原理实现

74 阅读1分钟

🎯 一、为什么要掌握浅拷贝?

  • 前端中对象复制是非常常见的操作(表单处理、状态管理)
  • Object.assign 被大量使用,但很多人误以为它是深拷贝
  • 面试中经常考:浅拷贝和深拷贝的区别?手写 assign 的实现?

📌 二、什么是浅拷贝?

拷贝第一层属性,引用类型(如数组、对象)仍然共享引用,不递归。

示例:

const obj1 = { a: 1, b: { c: 2 } };
const obj2 = Object.assign({}, obj1);
obj2.b.c = 100;
console.log(obj1.b.c); // ❗️100,被修改了

🧠 三、Object.assign 的核心行为

行为是否支持
拷贝自身可枚举属性(不含继承)
支持多个源对象
拷贝 Symbol 属性
遇到同名属性后覆盖
是浅拷贝
会跳过 null/undefined 源对象

✍️ 四、手写 shallowClone(基础浅拷贝)

function shallowClone(obj) {
  if (typeof obj !== 'object' || obj === null) return obj;

  const result = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      result[key] = obj[key]; // 不递归,只拷贝第一层
    }
  }
  return result;
}

✅ 五、手写 Object.assign 简易版

function myAssign(target, ...sources) {
  if (target == null) throw new TypeError('Cannot convert undefined or null to object');
  const to = Object(target);

  for (const source of sources) {
    if (source == null) continue;

    const keys = [...Object.keys(source), ...Object.getOwnPropertySymbols(source)];

    for (const key of keys) {
      if (Object.prototype.propertyIsEnumerable.call(source, key)) {
        to[key] = source[key];
      }
    }
  }

  return to;
}

✅ 六、验证用例

const sym = Symbol('a');
const result = myAssign({}, { a: 1, [sym]: 2 });
console.log(result); // { a: 1, [Symbol(a)]: 2 }

❗ 七、和 Object.assign 的行为差异陷阱

场景正确行为
nullundefined 作为源跳过不报错
目标为 null/undefined报错:TypeError
Symbol key 属性也会被拷贝(但必须是 enumerable)
多个源对象从左往右依次覆盖同名属性

🧩 八、进阶建议:模拟拷贝工具库

  • 封装 shallowClone / deepClone

  • 提供可配置选项:

    • 是否保留 symbol?
    • 是否拷贝 getter/setter?
    • 是否冻结对象(Object.freeze)?