Object.assign只复制对象自身且可枚举的属性,跳过不可枚举属性、原型属性及Symbol键等;其底层逻辑等价于遍历Object.keys()结果赋值,而Object.keys()本身不包含不可枚举属性。

Object.assign 确实只处理“自身 + 可枚举”的属性,不可枚举属性(比如 enumerable: false 的属性、constructor、__proto__ 等内置属性)它直接跳过,不会复制。
不可枚举属性为什么被跳过
它的底层逻辑等价于遍历 Object.keys(source) 的结果再赋值——而 Object.keys() 本身就不包含不可枚举属性,所以 Object.assign 根本接触不到它们。即使属性真实存在于对象上、能读能写,只要 propertyIsEnumerable(key) === false 或不在自有属性中,就不会被拷贝。
验证某个属性是否会被 Object.assign 拷贝
可以用这个判断函数快速确认:
function isCopiedByAssign(obj, key) {
return obj.hasOwnProperty(key) && obj.propertyIsEnumerable(key);
}
返回 true:一定会出现在 Object.assign({}, obj) 的结果里
返回 false:无论值是什么,都不会被复制(可能是不可枚举、也可能是原型属性、或两者兼有)
手动拷贝不可枚举属性的方法
需要绕过 Object.assign 的限制,用更底层的 API:
- 用
Object.getOwnPropertyDescriptors(obj)获取对象所有自有属性的完整描述符(含enumerable: false的) - 再用
Object.defineProperties(target, descriptors)把这些描述符批量定义到目标对象上 - 如果要合并多个源对象,需循环调用
getOwnPropertyDescriptors,并自行处理键冲突(比如跳过、覆盖或合并)
注意:Symbol 键、getter/setter 访问器属性同样不会被 Object.assign 处理,也需要通过 getOwnPropertyDescriptors 显式获取和定义。
常见不可枚举属性的来源
这些情况容易被忽略但确实不被拷贝:
- 用
Object.defineProperty(obj, 'key', { value: ..., enumerable: false })显式设置的属性 - 类的原型方法(如
Person.prototype.sayHi),属于继承属性,不是自有属性 - 部分内置属性,如
Array.prototype.length、Function.prototype.name等 - 通过
Object.create(null)创建的对象没有原型,但其自有属性若设为不可枚举,依然不会被拷贝

















