要保留属性描述符特性,必须显式重建描述符:用object.getownpropertydescriptors获取,按需修改(如writable: false、enumerable: false),再用object.defineproperty注入;不可配置属性后续无法修改,需谨慎设置configurable。

在对象拷贝过程中,直接使用浅拷贝(如 Object.assign)或深拷贝(如结构化克隆、第三方库)无法保留原始对象的属性描述符特性——比如 writable: false 或 enumerable: false。要真正实现“只读”或“隐藏”控制的注入,必须在拷贝时**显式重建属性描述符**,而非仅复制值。
用 Object.defineProperty 逐个重建带描述符的属性
这是最直接可控的方式。遍历源对象的自有属性描述符,再在目标对象上用 Object.defineProperty 重新定义:
- 调用
Object.getOwnPropertyDescriptors(obj)获取全部描述符(含value、writable、enumerable、configurable,对访问器属性还包含get/set) - 对每个描述符,检查是否需要保留或修改其控制逻辑(例如强制设
writable: false表示只读,设enumerable: false实现隐藏) - 用
Object.defineProperty(target, key, descriptor)注入到新对象
示例:将源对象所有属性设为只读并隐藏枚举
const source = { a: 1, b: 'hello' };Object.defineProperty(source, 'a', { writable: false });
Object.defineProperty(source, 'b', { enumerable: false });
const target = {};
const descriptors = Object.getOwnPropertyDescriptors(source);
for (const [key, desc] of Object.entries(descriptors)) {
Object.defineProperty(target, key, {
...desc,
writable: false, // 强制只读
enumerable: false // 强制隐藏
});
}
console.log(target.a); // 1
target.a = 99; // 无效果(非严格模式)或报错(严格模式)
console.log(Object.keys(target)); // [] —— 不出现
拷贝时过滤/转换特定描述符字段
不是所有属性都需要保留原始控制策略。可在注入前做语义判断:
- 识别敏感字段名(如
id、createdAt),自动设configurable: false和writable: false - 对以
_开头的属性(约定为内部字段),统一设enumerable: false实现逻辑隐藏 - 跳过
set/get访问器(避免拷贝后行为失真),只拷贝value并转为数据属性
结合类构造与初始化时的描述符注入
若拷贝发生在实例化阶段(如从 DTO 创建 UI 模型),可在构造函数中主动注入控制逻辑:
- 接收原始数据对象作为参数
- 用
Object.defineProperties(this, { ... })批量定义受控属性 - 对只读字段,不提供
set,或在set中抛出错误 / 忽略赋值
这种方式把“只读”“隐藏”逻辑封装进类型契约,比运行时动态注入更稳定。
注意边界:不可配置属性无法后期修改
一旦设了 configurable: false,该属性就无法再被 delete,也无法再用 Object.defineProperty 修改其 enumerable、writable 或 configurable 特性。所以:
- 若需后续动态放开只读,初始不要设
configurable: false,只设writable: false - “隐藏”靠
enumerable: false即可,不影响读写,也不影响configurable后续调整 - 批量注入前建议先清理目标对象已有同名属性(
delete target[key]),避免因configurable: false导致定义失败











