getter/setter 默认定义在类的 prototype 上,通过原型链委托执行;实例赋值会遮蔽原型访问器,导致校验失效;子类需显式重定义才能覆盖;私有字段不可被子类访问,应抽离可复用逻辑为受保护方法。

类定义中的 getter/setter 默认被添加到类的 prototype 上,不是实例自身属性,而是通过原型链动态委托执行的。
它们不复制,只委托
当你写:
class Person {
constructor(name) { this._name = name; }
get name() { return this._name; }
set name(v) { this._name = v.trim(); }
}JavaScript 引擎会把 get name() 和 set name() 作为两个访问器属性,定义在 Person.prototype 上。每个实例(如 new Person('Alice'))自身没有 name 属性,读写时自动沿原型链找到 prototype 上的访问器函数执行。
遮蔽比继承更常见
一旦你在实例上直接赋值:person.name = 'Bob',且原型上没有 setter(或 setter 未处理该逻辑),引擎会在 person 自身创建一个**数据属性** name,覆盖(遮蔽)原型上的 getter/setter。此后 person.name 就不再触发任何函数,只是普通读写。
- 这是静默发生的,容易导致预期外的行为(比如校验失效、计算逻辑跳过)
- 若希望禁止遮蔽,可在 prototype 上用
Object.defineProperty显式设置writable: false或configurable: false
子类重写需显式声明
子类无法“自动继承并增强”父类的 getter/setter。必须在子类中重新定义同名访问器才能覆盖:
class Employee extends Person {
get name() {
return `[EMP] ${super.name}`; // 必须显式调用 super
}
set name(v) {
super.name = v.toUpperCase();
}
}- 没定义
get name()的子类,仍委托父类 prototype 的 getter - 只定义
set name()而不定义get name():读取会返回undefined - 用
class语法定义的访问器默认configurable: true,支持后续重定义;手动用defineProperty添加则需自行设configurable: true才能重写
私有字段与访问器不跨类可见
如果 getter/setter 内部访问的是私有字段(如 #id),那它只能在定义它的类内部使用。子类中既不能读取该私有字段,也不能定义同名访问器去“扩展”它——因为私有字段根本不可见。
- 想让子类参与逻辑,应把可复用的部分抽成受保护的普通方法(如
_validate()),再由 public 访问器调用 - 避免在 getter 中直接修改外部状态(如发请求、改 DOM),否则子类无法拦截或干预

















