reflect 本身无法直接实现属性复制,需结合 reflect.ownkeys 和 reflect.getownpropertydescriptor 获取并还原所有自有属性(含 symbol 和不可枚举属性)的完整描述符,再用 reflect.defineproperty 精确复制元信息。

使用 Reflect 本身无法直接实现属性复制,因为 Reflect 是一套用于操作对象的静态方法集合(如 Reflect.get、Reflect.set),它不提供“遍历并复制所有属性”的能力。真正实现通用属性复制,需要结合 Reflect.ownKeys(获取自有属性键)与 Reflect.getOwnPropertyDescriptor(获取属性描述符),再用 Object.defineProperty 或 Reflect.defineProperty 进行精确复制——尤其是保留 getter/setter、可枚举性、可配置性等元信息。
获取所有自有属性键(含 Symbol)
Reflect.ownKeys(target) 是关键起点,它能同时返回字符串键和 Symbol 键,比 Object.keys() 或 Object.getOwnPropertyNames() 更完整:
- 返回数组,包含所有自有属性键(包括不可枚举的和 Symbol 类型)
- 顺序遵循:先按创建顺序的整数索引键 → 其他字符串键 → Symbol 键
- 这是实现“真正通用”复制的前提,否则 Symbol 属性会被遗漏
逐个读取并还原属性描述符
对每个 key,调用 Reflect.getOwnPropertyDescriptor(target, key) 获取完整描述符(含 value、writable、get、set、enumerable、configurable)。注意:
- 如果描述符为
undefined,说明该 key 是继承属性,不应复制(ownKeys已保证是自有属性,此情况极少) - 若属性是 accessor(含
get或set),不能简单用target[key]读值,必须通过描述符还原 - 需原样传递描述符给
Object.defineProperty或Reflect.defineProperty
目标对象需支持定义属性(避免静默失败)
复制前应确保目标对象可扩展(Object.isExtensible(to)),且对每个 key 有权限设置属性。建议加上容错处理:
- 用
Reflect.defineProperty(to, key, desc)替代Object.defineProperty,它返回布尔值表示是否成功 - 若失败(如目标为冻结对象),可选择跳过、抛错或降级为简单赋值(仅适用于数据属性)
- 避免直接覆盖已有 accessor,除非明确允许(可通过
desc.configurable判断)
完整示例:深拷贝无关,专注属性行为复制
以下函数实现“浅层、全量、保元信息”的属性复制(不递归,不处理原型链):
function copyOwnProperties(to, from) {
const keys = Reflect.ownKeys(from);
for (const key of keys) {
const desc = Object.getOwnPropertyDescriptor(from, key);
if (desc) {
Reflect.defineProperty(to, key, desc);
}
}
return to;
}
它能正确复制:Symbol 属性、不可枚举属性、getter/setter、不可配置属性——这是 Object.assign 或展开运算符做不到的。











