普通浅拷贝会丢失属性描述符,应使用Object.getOwnPropertyDescriptors+Object.defineProperties实现保真浅拷贝,该方法100%复制自有属性的所有descriptor,不处理原型链属性。

普通浅拷贝(如 Object.assign 或展开运算符 {...obj})会丢失属性描述符——比如 get/set、enumerable: false、writable: false、configurable: false 等关键元信息。要真正保留这些,必须绕过“只取值”的路径,直接操作属性的完整描述结构。
用 Object.getOwnPropertyDescriptors + Object.defineProperties
这是目前标准、无依赖、保真度最高的浅拷贝方式,能 100% 复制自有属性的所有描述符。
- 先调用
Object.getOwnPropertyDescriptors(obj)获取源对象每个自有属性的完整 descriptor 对象(含value、get、set、enumerable、writable、configurable) - 再用
Object.defineProperties({}, descriptors)把这些 descriptor 逐个定义到新对象上 - 注意:该方法不处理原型链上的属性,只作用于对象自身的属性,符合“浅拷贝”定义
哪些情况会丢描述符?要避开这些写法
以下常见操作看似是复制,实则只读取并赋值属性的 运行时结果,彻底抛弃原始定义逻辑:
-
Object.assign({}, obj):对访问器属性会立即执行get取值,新对象上变成普通value属性,set消失 -
{...obj}:同上,且不可枚举属性(enumerable: false)根本不会出现在展开结果中 -
JSON.parse(JSON.stringify(obj)):连基本类型都受限,更别说描述符;仅适用于纯数据对象
手动补全不可枚举或受控属性(兜底场景)
如果无法使用 getOwnPropertyDescriptors(例如需兼容极老环境),可针对性修复几类高频丢失项:
- 对已知的不可枚举属性,用
Object.defineProperty显式重定义,并设enumerable: false - 对有
get/set的关键字段,不要展开,改用Object.defineProperty单独迁移整个访问器 - 冻结/密封对象(
Object.freeze)拷贝后需再次调用对应方法,因为assign和展开不继承configurable/writable状态
验证是否真的保真
拷贝完成后,建议用以下方式交叉检查关键描述符是否一致:
-
Object.getOwnPropertyDescriptor(newObj, 'prop')对比源对象同名属性的 descriptor -
Object.keys(newObj)和Object.getOwnPropertyNames(newObj)比较,确认不可枚举属性是否仍在 - 尝试修改属性值、删除属性、重新定义,观察是否触发预期行为(如
writable: false下赋值是否静默失败)


















