这不是字段丢失,而是原型链被暴力重写导致属性查找失效;应先用Object.getOwnPropertyNames检查自有属性是否存在,再通过Object.getPrototypeOf确认原型是否被覆盖,最后分步修复原型链并规避setPrototypeOf滥用。

这种情况不是“字段丢失”,而是原型链被暴力重写后,对象原有的属性访问路径被破坏,导致看似字段没了——实际是查找机制失效了。
先确认是不是真丢了字段
别急着修,先用 console.log(Object.getOwnPropertyNames(obj)) 查所有自有属性。如果字段还在列表里,说明它们没丢,只是原型链断了,方法调用和继承行为失效而已。
再查原型链:Object.getPrototypeOf(obj)。如果返回 null 或 Object.prototype,就坐实了:你给一个还没 ready 的对象强行塞了新原型,它原本该有的 prototype 被覆盖了。
临时对象必须分阶段初始化
受控对象(比如表单模型、配置代理、状态容器)不该在构造中途就被塞进原型链。正确顺序是:
- 先确保实例基本结构完整:所有自有属性已赋值,必要内部状态已建立
- 再设置原型:仅在
constructor末尾或init()完成后执行Object.setPrototypeOf - 避免在 getter/setter、事件回调、异步 resolve 里做这事——那些时刻对象往往处于中间态
补救:两步还原,不碰已有字段
既然字段还在,修复目标就是把原型链接回去,让方法和继承恢复可用:
- 第一步:用
Object.setPrototypeOf(obj, CorrectConstructor.prototype)把原型指回去 - 第二步:检查
CorrectConstructor.prototype.constructor是否指向正确构造函数;若被覆盖过,手动设回:CorrectConstructor.prototype.constructor = CorrectConstructor - 注意:不要对
obj.constructor直接赋值——它只是个只读快照,改了也没用
长期规避:换掉 setPrototypeOf
对临时对象动 Object.setPrototypeOf 是高危操作。更稳的替代方式:
- 创建时指定原型:
const obj = Object.create(MyClass.prototype),再Object.assign(obj, { ...initialData }) - 用类声明 +
extends,让引擎自动处理原型链,不依赖运行时修补 - 需要动态能力?用
Proxy拦截get,按需委托到目标原型,不改动真实 [[Prototype]] - 纯数据容器场景,直接用
Object.create(null),彻底避开原型污染风险

















