最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何用 Object.getOwnPropertyDescriptors 完美克隆包含 Getter/Setter 的复杂对象
时间:2026-07-27 09:36:47 编辑:袖梨 来源:一聚教程网
Object.getOwnPropertyDescriptors能获取getter/setter,因为它返回完整属性描述符(含get、set等元信息),而Object.assign和展开运算符仅复制值且忽略访问器;克隆必须用Object.defineProperties配合其结果。
Object.getOwnPropertyDescriptors 为什么能拿到 getter/setter
因为 Object.assign 和展开运算符({...obj})只复制属性的「值」,对 get / set 函数直接忽略——它们根本不会被遍历到。而 Object.getOwnPropertyDescriptors 返回的是完整属性描述符对象,包括 get、set、enumerable、configurable 等元信息,这才是还原访问器属性的唯一可靠入口。
克隆时必须用 Object.defineProperties 而非 Object.assign
Object.assign 会把 getter 当作普通值调用一次,然后把返回结果赋给目标属性;setter 更是直接丢失。正确做法是把 Object.getOwnPropertyDescriptors 的结果原样传给 Object.defineProperties:
const source = { _x: 10, get x() { return this._x * 2; }, set x(v) { this._x = v / 2; }};<p>const descriptors = Object.getOwnPropertyDescriptors(source);const clone = Object.defineProperties({}, descriptors);</p><p>// ✅ clone.x 是响应式的,修改 clone.x 会触发 setter// ❌ 用 Object.assign({}, source) 得到的是 { x: 20 } —— 静态值,无访问器
深层克隆需递归处理,但 descriptor 不含原型链信息
Object.getOwnPropertyDescriptors 只作用于对象自身的属性,不包含继承来的 getter/setter,也不处理嵌套对象。如果要深克隆,得手动递归判断:
- 遇到普通对象且非 null,先用
Object.getOwnPropertyDescriptors获取自身描述符 - 对每个描述符的
value字段,如果是对象/数组,递归克隆 - 对
get或set字段,保持原函数引用(不能执行,也不能深克隆函数体) - 注意:
defineProperties不会自动设置prototype,若需继承源对象原型,得额外用Object.setPrototypeOf(clone, Object.getPrototypeOf(source))
容易漏掉的三个坑
实际写的时候,这几个点几乎必踩:
-
enumerable和configurable默认为false,如果源属性是true却没透传,克隆后属性可能不可枚举或不可删除 - Symbol 属性不会被
Object.getOwnPropertyDescriptors返回(它只处理字符串键),需要用Object.getOwnPropertySymbols单独补全 - 如果源对象有
[[Get]]但没[[Set]](即只读访问器),克隆后仍应保持只读;但若误把set: undefined传给defineProperties,会静默转成可写数据属性
真正“完美”克隆不是靠一个 API,而是理解 descriptor 的完整结构,并在 define 时严格保留每个字段的原始语义。否则看着像,一改值或一 for...in 就露馅。