ES6. Class constructor question
Здравствуйте.
У меня условно абстрактный класс для работы с сетью, от которого наследуется множество классов различных моделей, пример: /** * @abstract */ class AbstractHttpService { baseRouteName; constructor(baseRouteName) { if (baseRouteName) { this.baseRouteName = baseRouteName; } } } class ProductsHttpService extends AbstractHttpService { baseRouteName = 'product'; } Решил вкорячить кэширование экземпляров http-сервисов и тут понял, что из конструктора абстрактного класса никак не получить значение свойства "ребенка" до его инициализации. Скажите, пожалуйста, есть ли какой-нибудь способ получить из абстрактного класса значение свойства класса, который наследуется от этого самого абстрактного класса? Вижу только одно решение: вместо объявления для ребенка значения свойства по умолчанию передавать в конструктор родителя это самое значение. |
Цитата:
И то, что js глотает такую конструкцию - это скорее бага, чем фитча. Не должны переобъявляться поля базовых классов в наследниках. Только так class AbstractHttpService { baseRouteName; constructor(baseRouteName) { this.baseRouteName = baseRouteName; } } class ProductsHttpService extends AbstractHttpService { constructor(baseRouteName) { super (baseRouteName)} } |
Цитата:
В каких языках переопределение свойства базового класса приведет к ошибке? |
Ну в C++ переобъявить просто не удастся. Будет поле в базом классе и поле с таким же именем в наследнике.
В js с методами так работает, а вот со свойствами нет. Свойство будет одно. Вроде ошибки нет, но фигня может получиться. Если я беру чужой базовый класс, то придется скурпулезно изучить, какие у него поля, что бы случайно не написать такое же имя и работать с ним, как со своим. Приватные свойства спасают. Там будет у базового класса свое, у наследника свое. Но из наследника к свойству базового не обратиться. protected в js еще не придумали. |
voraa, если и в других языках ошибки нет, значит в и js это все-таки не бага.
Цитата:
ps. в php базовый класс может работать со свойствами наследника прямо из конструктора и, имхо, это очень удобно, не нужно нагружать конструктор метода всяким мусором или каким-либо другим способом передавать классу его "настройки". |
Цитата:
Но даже если знать, что у наследника должно быть поле с определенным именем, то в js сначала выполняется конструктор базового класса, а потом конструктор наследника. И при работе конструктора базового, полей наследника просто не существует. |
Цитата:
<?php abstract class AbstractHttpService { protected string $baseRouteName; public function __construct(?string $baseRouteName = null) { if ($baseRouteName) { $this->baseRouteName = $baseRouteName; } var_dump($this->baseRouteName); } } class ProductsHttpService extends AbstractHttpService { protected string $baseRouteName = 'product'; } new ProductsHttpService();// string(7) "product" |
Почти изящное решение
/** * @abstract */ class AbstractHttpService { baseRouteName; constructor(baseRouteName) { this.baseRouteName = baseRouteName ?? new.target.baseRouteName; } } class ProductsHttpService extends AbstractHttpService { static baseRouteName = 'product'; } let x = new ProductsHttpService () console.log (x.baseRouteName) |
voraa, я уже заменил переопределение свойства базового класса в наследуемом классе на передачу аргумента в конструктор.
Спасибо за пример решения, не знал, что у оператора new есть свойство target. |
Я даже не знаю, как назвать конструкцию new.target. В документациях это называется мета-свойство. Вроде как в Js у операторов не бывает свойств и ничего другого вида <оператор>.<свойство> не встречается. Просто данность такая.
|
Есть еще один способ добраться из базового класса, до класса самого верхнего наследника.
this.constructor Причем, это прокатывает даже в определении полей, в отличие от new.target, который работает только непосредственно в конструкторе /** * @abstract */ class AbstractHttpService { baseRouteName = this.constructor.baseRouteName; constructor(baseRouteName) { if (baseRouteName) this.baseRouteName = baseRouteName; } } class ProductsHttpService extends AbstractHttpService { static baseRouteName = 'product'; } let x = new ProductsHttpService () console.log (x.baseRouteName) |
может, так?
class AbstractHttpService { get baseRouteName() { return ''; } } class ProductsHttpService extends AbstractHttpService { get baseRouteName() { return 'product'; } } let x = new ProductsHttpService () console.log (x.baseRouteName) |
Ну если сеттера не нужно.
А если нужен, то надо где то хранить значение. И опять проблема, как установить начальное. |
Часовой пояс GMT +3, время: 22:17. |