子类无法重写父类私有字段的原因及实现预期行为的方案
嘿,我来帮你把这个问题理清楚~
首先,你遇到的核心问题是:JavaScript的私有字段(带#前缀的)是严格绑定到定义它们的类的。也就是说,A类里的#abc和B类里的#abc完全是两个毫不相干的字段——它们名字一样,但作用域被限制在各自的类内部,互相不可访问。
父类A的hellos方法里的this.#abc,只会指向A类自己定义的那个私有字段,永远不会去子类B里找同名的#abc。同样,A类里的私有getter#abcd,也只能访问A类的#abc,哪怕子类B也定义了自己的#abcd,父类的getHellos方法调用的还是A类的那个getter,因为私有成员只能在定义它们的类内部被访问。
那怎么实现你想要的“子类自定义值,父类方法使用子类值”的行为呢?这里有几个可行的方案:
方案1:使用受保护成员(语义约定)
如果不需要绝对严格的私有性,你可以用JavaScript社区的约定:用下划线_开头的变量表示“受保护”成员——虽然不是真正的私有,但语义上告诉其他开发者这个成员只供子类或内部使用,子类可以安全地重写它。
修改后的代码如下:
class A { _abc = 'hello from a'; hellos() { console.log(this._abc); } getHellos() { console.log(this._abcd); } get _abcd() { return this._abc; } } class B extends A { _abc = 'hello from b'; get _abcd() { return this._abc; } } const b = new B(); b.hellos(); // 输出 'hello from b' b.getHellos(); // 输出 'hello from b'
这里父类的方法通过this访问_abc,而this指向的是子类B的实例,所以会使用子类重写后的_abc值。
方案2:通过可重写的方法封装私有字段
如果你确实需要保持字段的私有性(不想让外部访问),可以在父类里定义一个可被重写的公共方法,用来获取这个私有值,子类重写这个方法返回自己的私有字段。
示例代码:
class A { #abc = 'hello from a'; // 定义一个可被子类重写的方法,返回私有字段的值 getAbcValue() { return this.#abc; } hellos() { console.log(this.getAbcValue()); } getHellos() { console.log(this.getAbcdValue()); } getAbcdValue() { return this.getAbcValue(); } } class B extends A { #abc = 'hello from b'; // 重写父类的方法,返回自己的私有字段 getAbcValue() { return this.#abc; } getAbcdValue() { return this.getAbcValue(); } } const b = new B(); b.hellos(); // 输出 'hello from b' b.getHellos(); // 输出 'hello from b'
这种方式既保留了父类和子类字段的私有性,又通过多态实现了父类方法使用子类值的效果。
设计建议
如果你的需求是让子类能够自定义某个成员的值,从而改变父类方法的行为,不要使用私有字段——私有字段的设计初衷就是完全封装,不让外部(包括子类)访问或修改。
优先选择:
- 语义化的受保护成员(下划线前缀),适合大多数需要子类扩展的场景;
- 可重写的方法封装私有字段,适合需要严格私有且支持扩展的场景。
备注:内容来源于stack exchange,提问作者pery mimon

