子类必须成对重写父类的 getter 和 setter,否则读取属性会返回 undefined,造成“写进去、读不出”的逻辑断层;推荐通过 super 显式委托父类逻辑,或明确抛错/返回占位值,并在测试中验证访问器完整性。

子类只重写父类的 setter 而漏掉 getter,会导致读取该属性时返回 undefined,看似正常却隐藏数据不一致风险。这不是语法错误,但会切断访问器语义链,必须成对处理。
必须成对定义访问器
JavaScript 中访问器属性(get/set)是独立函数,单边定义无法维持读写一致性:
- 父类有
get name()和set name(val),子类只写了set name(val)→ 读obj.name会沿原型链找不到get,直接返回undefined - 写操作仍触发子类
set,但读不到值,造成“写进去、读不出”的逻辑断层 - 这种行为不会报错,容易在测试中被忽略,上线后引发数据展示异常或计算错误
推荐重写方式:显式委托 super
最安全的做法是在子类中完整重写两个存取器,并通过 super 显式调用父类逻辑:
get name() { return super.name; }set name(val) { super.name = val; }- 若需扩展逻辑(如校验、日志),在对应方法内添加,再调用
super - 避免只覆盖一方,也不要用空
get或默认返回undefined
若确实不需要读取,应主动设防
有些场景下子类明确禁止读取,这时不能留白,而要给出明确语义:
- 返回有意义的占位值,比如空字符串
return '';或null(需文档说明) - 抛出错误:
throw new Error('name is write-only in this subclass'); - 避免让 JS 默认回退到
undefined,那会掩盖设计意图,也难被类型检查捕获
预防机制:加测试验证描述符完整性
仅靠人工检查易遗漏,建议在单元测试中加入访问器完整性断言:
- 检查子类实例是否同时拥有
get和set:Object.getOwnPropertyDescriptor(Sub.prototype, 'name') - 验证读写是否都可执行且不报错:
expect(() => obj.name).not.toThrow(); expect(() => obj.name = 'x').not.toThrow(); - 对关键业务属性做必检项,防止 CI/CD 流程中引入隐性漏洞










