Object.assign 和 Object.keys + for...of 均只拷贝对象自身的可枚举字符串属性,不处理原型、Symbol、不可枚举属性及递归;前者最简,后者更透明可控。

可以使用 Object.assign 或 Object.keys + for...of 配合 hasOwnProperty(虽通常冗余)来实现——但关键在于**只遍历对象自身的可枚举属性**,不继承、不处理 Symbol、不递归。
用 Object.assign 实现(最简且符合要求)
Object.assign 默认只拷贝源对象的**自有可枚举属性**(即满足 obj.propertyIsEnumerable(key) && obj.hasOwnProperty(key) 的属性),不会复制原型链上的属性,也不会处理不可枚举或 Symbol 属性。它本质就是浅拷贝。
示例:
function shallowClone(obj) {
if (obj == null || typeof obj !== 'object') return obj;
return Object.assign({}, obj);
}
const parent = { a: 1 };
const child = Object.create(parent);
child.b = 2;
child.c = 3;
Object.defineProperty(child, 'd', { value: 4, enumerable: false });
console.log(shallowClone(child)); // { b: 2, c: 3 } —— 只有自有可枚举属性
手动遍历自有可枚举属性(更透明可控)
用 Object.keys() 获取所有自有可枚举字符串键,再逐个赋值。它天然排除原型属性、不可枚举属性和 Symbol 属性,语义清晰。
-
Object.keys(obj)返回的数组只包含对象自身的可枚举字符串属性名 - 循环中直接读取并写入新对象,保证浅拷贝语义
- 支持 null/undefined/非对象输入的防御性检查
function shallowClone(obj) {
if (obj == null || typeof obj !== 'object') return obj;
const result = {};
for (const key of Object.keys(obj)) {
result[key] = obj[key];
}
return result;
}
注意:这些方法都不处理 Symbol 和不可枚举属性
如果明确需要包含 Symbol 属性,需额外使用 Object.getOwnPropertySymbols();若还需不可枚举属性,则要用 Object.getOwnPropertyDescriptors() 并配合 Object.defineProperties()。但题目要求“只拷贝自有属性”,而 JS 中“自有属性”默认指 Object.keys + Object.getOwnPropertySymbols 覆盖的范围——不过常规场景下,Object.keys 已满足绝大多数“自有可枚举字符串属性”的需求。
不推荐用 JSON.parse(JSON.stringify())
- 会丢掉函数、undefined、Symbol、Date、RegExp 等类型
- 会跳过不可枚举属性,但也会跳过原型属性——看似“自有”,实则机制不同且副作用大
- 不是真正的属性遍历逻辑,不符合“显式只拷贝自有属性”的设计意图


















