Извикване на родителска функция, която се отменя от дете по време на веригата на конструктора в JavaScript(ES6)

Срещнах проблем по-долу с JavaScript(ES6)

class A{
  constructor(){
    this.foo();
  }
  foo(){
    console.log("foo in A is called");
  }
}

class B extends A{
  constructor(){
    super();
    this.foo();
  }
  foo(){
    console.log("foo in B is called");
  }
}

Това, което очаквам е

foo in A is called
foo in B is called

Но всъщност е така

foo in B is called
foo in B is called

Знам, че мога да разреша това, като просто добавя super.foo() във функцията foo на клас B

class B extends A{
  constructor(){
    super();
    this.foo();
  }
  foo(){
    super.foo() // add this line
    console.log("foo in B is called");
  }
}

Но си представете сценарий, подобен на този:

Детето трябва да замени функцията на родителя, за да извърши някои допълнителни дейности и да предотврати достъпа на външен достъп до оригиналния.

class B extends A{
  constructor(){
    super();
    this.initBar();
  }
  foo(){
    super.foo();
    this.bar.run(); //undefined
    console.log("foo in B is called");
  }
  initBar(){
    this.bar.run = function(){
      console.log("bar is running");
    };
  }
}

Изглежда, че this все още сочи към дете B, докато конструира в родител A. Ето защо не мога да се свържа с foo на родител A.

Как да накарам this да извика функцията на родителската версия, която се отменя от дъщерна по време на веригата на конструктора?

Или има по-добро решение, когато става въпрос за сценарий като този?

Редактиране

И така, след като прочетох отговорите, основният въпрос става...

Не е препоръчително да се поставят initialize helpers или setter functions в конструктора в JavaScript, тъй като децата имат шанс да ги заменят?

За да изясня по-ясно ситуацията: (съжалявам за предишния ми лош пример :( )

class A{
  constructor(name){
    this.setName(name);
  }
  setName(name){
    this._name = name;
  }
}

class B extends A{
  constructor(name){
    super(name);
    this._div = document.createElementById("div");
  }
  setName(name){
    super.setName(name);
    this._div.appendChild(document.createTextNode(name));
  }
}

new B("foo")

this._div ще бъде undefined.

Това лоша идея ли е, тъй като детето ще може да замени функцията?

class A{
  constructor(name){
    this.setName(name); // Is it bad?
  }
  ...
}

Така че не трябва да използвам initialize helpers или setter functions в конструктора като в Java, C++...?

Трябва ли ръчно да извиквам неща като това new A().init(), за да ми помогне да инициализирам нещата?


person andrew    schedule 16.09.2015    source източник
comment
Как да накарам this да извика функцията на родителската версия - super е единственият начин.   -  person thefourtheye    schedule 16.09.2015
comment
Защо конструкторът A изобщо извиква this.foo()? Не трябва, трябва само да инициализира инстанцията.   -  person Bergi    schedule 16.09.2015
comment
Детето foo предназначено ли е да замени изпълнението на родител в общата кауза, или по-скоро питате как да направите foo някакъв частен помощник?   -  person loganfsmyth    schedule 16.09.2015
comment
Синтаксисът на клас ES6 все още генерира прототип, точно както използвахме за ръчен код. Така че, когато дефинирате foo в клас B, той все още отменя дефиницията на foo в прототипа и този this.foo() независимо къде го извиквате ще се отнася до дефиницията на клас B. Както изглежда вече знаете, ако искате да извикате клас А версия на foo, трябва да направите това ръчно.   -  person jfriend00    schedule 16.09.2015
comment
FYI, изглежда мислите, че има два обекта, един за дете и един за родител. Това не е начинът, по който работи. this не сочи към дете B. this сочи към целия обект, от който и A, и B имат свойства на екземпляра и методи на прототипа. Има само един обект с A и B допринасящи свойства и методи. Ето защо също има само едно this и защо this.foo сочи към най-скорошната отмяна.   -  person jfriend00    schedule 16.09.2015
comment
Вероятно си струва да се отбележи, че трябва да получите абсолютно същия резултат с еквивалентен код в Java, Python и C++.   -  person cosmicFluke    schedule 09.06.2016


Отговори (1)


Изглежда работите с погрешно схващане, че има два обекта A и B, когато сте в конструктора на производния клас B. Това изобщо не е така. Има един и само един обект. И A, и B допринасят със свойства и методи към този обект. Стойността на this ще бъде същата в конструктора за B, както е в конструктора за A по време на създаването на обект от клас B.

Синтаксисът на класа ES6 е само захар над метода ES5 за използване на прототипи за типове обекти и всъщност прототипът все още се използва под завивките. Като такъв, когато дефинирате метод foo в производен клас, както правите в клас B, той все още се присвоява към прототипа и това присвояване отменя всеки метод със същото име, който може вече да съществува в прототипа, който идва от родителската дефиниция . Ето защо this.foo() се отнася за клас B версия на foo. Ако искате да достигнете до клас А версия на foo, тогава ще трябва ръчно да посочите това с помощта на super, както изглежда вече знаете.

Що се отнася до вашите конкретни въпроси:

Изглежда, че това все още сочи към дете B, докато конструира в родител A. Ето защо не мога да достигна до foo на родител A.

дете B и родител A не са отделни обекти. Има един обект, към който се отнасят както родител A, така и дете B. родителски A и дъщерен B методи или конструктори ще видят точно същата стойност на this.

Как да накарам това да извика функция на родителска версия, която се отменя от дъщерна по време на веригата на конструктора?

Използвате super за директно препращане към родителски методи, както изглежда вече знаете.

Или има по-добро решение, когато става въпрос за сценарий като този?

super е решението.


FYI, това е доста добра дискусия за класове ES6, включително как работи super: Класове в ECMAScript 6 (окончателна семантика). Раздел 4.4 изглежда особено подходящ за вашия въпрос/разбиране.

person jfriend00    schedule 16.09.2015
comment
Мисля, че е по-добре да не се използват сетери в родителския конструктор като this.setName(name); и ще бъде по-добре да използвате this.name = име; по този начин, когато използвате super(), няма да използвате функции за заместване, за които не знаете. - person llioor; 07.02.2017