🎯 一、为什么要掌握浅拷贝?
- 前端中对象复制是非常常见的操作(表单处理、状态管理)
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 的行为差异陷阱
| 场景 | 正确行为 |
|---|---|
null 或 undefined 作为源 | 跳过不报错 |
目标为 null/undefined | 报错:TypeError |
| Symbol key 属性 | 也会被拷贝(但必须是 enumerable) |
| 多个源对象 | 从左往右依次覆盖同名属性 |
🧩 八、进阶建议:模拟拷贝工具库
-
封装
shallowClone/deepClone -
提供可配置选项:
- 是否保留 symbol?
- 是否拷贝 getter/setter?
- 是否冻结对象(Object.freeze)?