класс B расширяет класс A. Я назову A родителем, а B дочерним. У обоих есть конструкторы. B вызывает super () внутри своего конструктора. Оба имеют метод с одинаковым именем. Возможно, по стечению обстоятельств или по ошибке оба имеют переменную this.x. Тогда нет возможности получить доступ к родительской переменной this.x. Затем он становится точкой, возможно, непреднамеренного общения между ребенком и родителем.
class A {
constructor(){
this.x = "super x!";
}
logx(){
console.log(this.x);
}
}
class B extends A{
constructor(){
super();
this.x = "derived x.";
}
logx(){
super.logx();
}
}
let b = new B;
b.logx(); // expected "super x!", but it prints "derived x".
Может случиться так, что класс А происходит из библиотеки или был написан кем-то другим. Может даже случиться так, что автор класса A придет, отредактирует код и добавит новую переменную, которая затем псевдонимом для ребенка, которого он даже не знает, существует. Затем автор дочернего класса должен стать заядлым читателем изменений в родительском классе, чтобы он или она могли соответствующим образом обновить свой собственный код, если даже этот автор все еще находится в проекте. (Это такая ошибка, которая привела меня сюда сегодня, это ее перегонка.)
В следующем коде я предотвращаю эту проблему, давая каждой переменной префикс, совпадающий с именем класса. Тогда я получаю ожидаемое поведение. Конечно, есть лучший способ. Возможно, некоторые из этих частных / публичных ключевых слов помогут?
constructor(){
this.A_x = "super x!";
}
logx(){
console.log(this.A_x);
}
}
class B extends A{
constructor(){
super();
this.B_x = "derived x.";
}
logx(){
super.logx();
}
}
let b = new B;
b.logx(); // expected "super x!", and indeed it prints "super x!"
Это также происходит для вызовов методов, хотя это не так удивительно, потому что а) это считается «полиморфизмом»; б) обычно изменения в интерфейсе вышестоящего кода имеют последующие эффекты кода. Однако у программиста могут быть некоторые вспомогательные функции, не предназначенные для интерфейса, и если автору дочернего класса случается подумать об одном и том же имени вспомогательной функции, или расширяет интерфейс функцией с этим именем ...
class A {
constructor(){
this.x = "super x!";
}
f(){
console.log("I am a super f()!");
}
logx(){
this.f(); // aliased - polymorphism behavior
console.log(this.x);
}
}
class B extends A{
constructor(){
super();
this.x = "derived x.";
}
f(){
console.log("I am a derived f()");
}
logx(){
super.logx();
}
}
let b = new B;
b.logx();
вывод на консоль:
I am derived f()
derived x.
Согласно комментариям Джонаса Уилмса о его раскручивании происходящего, верно, что шаблон композиции может использоваться для инкапсуляции данных родителя и, таким образом, предотвращения случайного наложения псевдонимов:
class A {
constructor(){
this.x = "super x!";
}
f(){
console.log("I am a super f()!");
}
logx(){
this.f();
console.log(this.x);
}
}
class B {
constructor(){
this.a = new A();
this.x = "derived x.";
}
f(){
console.log("I am a derived f()");
}
logx(){
this.a.logx();
}
}
let b = new B;
b.logx();
И ведет себя как положено, вывод консоли:
I am a super f()!
super x!
Однако это не без проблем. Во-первых, оператор instanceof не работает. Во-вторых, мы не наследуем любые методы. Автору дочернего класса придется добавить заглушки, которые просто принимают аргументы и передают их методу родительского класса. Это может повлиять на производительность. См. ES6 и соавт. Можно ли определить метод всеобщего охвата? .
... кажется, этот вопрос сводится к тому, «как вы определяете, что на интерфейсе, а что нет?» ну и дела, есть демонстрация того, почему кто-то может захотеть это сделать.