子类重写父类 setter 但未同步更新 getter,会导致读写行为错配、状态陈旧或副作用丢失;修复需成对维护访问器、抽离微任务逻辑为受保护方法并显式委托,且确保 configurable: true。

子类重写父类 setter 但未保留微任务逻辑,会导致读取时跳过异步处理流程,暴露原始值或未更新状态——这不是数据丢失,而是行为断层。核心在于:setter 被遮蔽后,getter 若仍依赖微任务链,就会读到陈旧、未处理或未校验的中间态。
遮蔽即断裂:访问器必须成对维护
父类 getter 内部用 queueMicrotask 或 Promise.then 延迟返回计算结果,而子类只重写了 setter 却没动 getter,就会造成“写走新路、读走老路”的错配:
- 写操作绕过微任务(比如直接赋值
this._raw = val),但读操作仍等待 Promise 完成 → 读到的是上一次微任务的结果 - 若父类 getter 本身也依赖 setter 写入的中间状态(如标记位、缓存键),而子类 setter 没同步更新这些状态 → getter 返回 undefined 或默认值
- 更隐蔽的情况:父类 setter 触发了校验/转换/日志等微任务,子类重写后这些副作用全部消失,外部调用方无法感知
修复方式:强制复用 + 显式委托
不能只改 setter,必须让整个属性访问链保持一致。推荐两种落地做法:
- 在父类中把微任务核心逻辑抽为受保护方法(如
_updateAsync(val)返回 Promise),getter 和 setter 都调用它;子类重写 setter 时,必须调用super._updateAsync(val)并在其 Promise 上扩展,而不是另起赋值逻辑 - 若必须重写 setter,务必同步重写 getter,并确保二者共用同一套状态字段和触发条件。例如:父类用
this._pendingValue+this._resolvedValue双字段管理,子类 setter 更新前者,getter 等待后者就绪 —— 两者字段名与更新时机必须严格对齐
配置层面兜底:确保可重定义
如果父类用 Object.defineProperty 定义访问器,默认 configurable: false,子类无法重写同名 setter,会静默失败或抛错。必须显式设为 true:
- 在父类原型上定义时,加上
{ configurable: true, enumerable: true } - 使用 class 语法时,虽然不能直接设 descriptor,但可通过在构造器中用 defineProperty 覆盖原型上的访问器来补救
验证是否修复:三步检查法
改完后不靠肉眼,用最小测试确认行为连贯:
- 执行
obj.prop = 'new'后立即console.log(obj.prop)—— 应输出 Promise 实例(非最终值),证明 getter 仍走异步路径 - 在 setter 内加
console.log('in setter'),在 getter 的微任务回调里加console.log('in microtask')—— 两者应按预期顺序触发 - 检查
Object.getOwnPropertyDescriptor(obj.__proto__, 'prop'),确认set和get都指向当前期望的函数,且configurable为true


















