如何避免JavaScript父类构造函数调用子类重写的方法?
问题解答
1. 你的操作没有错误,这是JavaScript类继承的正常行为
在ES6类的继承机制中,子类调用super()时,this已经绑定到子类实例上。因此父类构造器里调用的this.foobar()会优先匹配子类原型上的重写方法,这就是你看到输出B foobar的原因。
2. 几种更简便的解决方案
方案一:使用私有方法(推荐)
ES6私有方法(以#开头)仅能在父类内部访问,子类无法重写或调用,可彻底解决父类构造器方法被覆盖的问题:
class A { #foobar() { // 私有方法,子类无法访问 console.log("A foobar"); } constructor(x) { console.log("A constructor"); this.x = x; this.#foobar(); // 调用父类私有方法 } foobar() { // 对外暴露的公共方法,子类可正常重写 this.#foobar(); } } class B extends A { constructor(x, y) { console.log("B constructor"); super(x); this.y = y; } foobar() { console.log("B foobar"); } } let a = new A(123); let b = new B(123, 456);
输出结果:
A constructor A foobar B constructor A constructor A foobar
方案二:直接调用父类原型上的方法
如果不想使用私有方法,可以在父类构造器中明确指定调用父类原型的方法,绕开子类的重写逻辑:
class A { constructor(x) { console.log("A constructor"); this.x = x; A.prototype.foobar.call(this); // 直接调用父类原型方法 } foobar() { console.log("A foobar"); } } class B extends A { constructor(x, y) { console.log("B constructor"); super(x); this.y = y; } foobar() { console.log("B foobar"); } } let a = new A(123); let b = new B(123, 456);
输出与方案一一致,父类构造器会执行A foobar。
方案三:抽离构造器专属逻辑为内部函数
把父类构造器需要执行的逻辑抽成独立的内部函数,不挂载到原型上,子类无法访问和重写:
class A { constructor(x) { console.log("A constructor"); this.x = x; // 仅父类构造器可调用的内部函数 function foobar() { console.log("A foobar"); } foobar.call(this); } foobar() { console.log("A foobar"); } } class B extends A { constructor(x, y) { console.log("B constructor"); super(x); this.y = y; } foobar() { console.log("B foobar"); } } let a = new A(123); let b = new B(123, 456);
同样能实现父类构造器执行自身逻辑的效果。
内容的提问来源于stack exchange,提问作者TreeTownGreen
相关产品推荐
相关产品推荐

