getter 和 setter 通过原型链动态委托实现而非复制,子对象未定义同名访问器时始终委托执行;若直接赋值则创建数据属性遮蔽原型访问器,导致逻辑失效。

Getter 和 Setter 在 JavaScript 原型链中不是“被复制到子类”的属性,而是靠动态委托实现的——每次访问时,引擎沿原型链查找并执行对应函数。
访问器属性走的是委托,不是继承
原型链上的 getter/setter 不会像数据属性那样被子对象“拥有”,也不会在实例上自动创建副本。只要子对象自身没定义同名访问器,读写操作就始终委托给原型上的函数执行。
- 父类原型有
get fullName(),子实例调用obj.fullName就触发它 - 子对象若新增
fullName = "xxx"(无 setter),就会在自身创建一个数据属性,从此遮蔽原型的 getter,后续读取返回字符串而非执行逻辑 - 这种“从访问器降级为普通属性”的行为静默发生,容易引发 bug
重写与遮蔽有明确规则
子类可以显式定义同名访问器来覆盖原型行为,但要注意覆盖方式:
- 只定义
set name(value)而不定义get name():读取该属性会返回undefined - 用
class语法声明的访问器默认configurable: true,支持重写;用Object.defineProperty添加到原型时,若没设configurable: true,后续无法在子类中修改或删除 - ES6 类中可通过
super.xxx显式调用父类访问器,比如set name(v) { super.name = v.trim(); }
private 访问器不可见,protected 不存在
JavaScript 没有真正的 protected,只有 # 私有字段和私有方法。私有访问器(如 get #id())仅在定义它的类内部可访问,子类完全看不到,也无法调用或重写。
- 想让子类能扩展逻辑,应把校验、转换等通用行为放在 public getter/setter 中
- 若需子类参与控制,可用受保护的普通方法(如
_validateName())配合 public 访问器封装 - 避免在私有访问器里直接操作外部可见状态,否则子类无法感知或干预
混用数据属性和访问器要格外小心
这是最常见的陷阱之一:父原型用 getter 控制某个属性,子对象却直接赋值,结果可能意外切断委托链。
- 例如父类
get price()返回计算值,子对象执行obj.price = 99,若没有 setter,就会在自身创建price数据属性,之后读取不再触发计算逻辑 - 防范方式:要么提供明确的 setter 并做校验,要么在 setter 中抛错提示,或用
Object.defineProperty把该属性设为writable: false防止意外覆盖 - 开发时建议统一用 class 语法定义访问器,结构清晰,
super调用自然,不易出错
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











