ES6: Как сделать фабрику методов для класса?
Вопрос на самом деле по TypeScript, но он в этом плане полностью копирует ES6.
Есть какой-то класс:
class Dog {
bulk() {
...
}
}
Нужно определять его методы не вручную, а динамически, примерно так:
function CreateMethod(params) {
return function() {
...
}
}
class Dog {
bulk: CreateMethod({name: 'bulk', volume: 90}) //не работает
}
Как это сделать без декораторов методов? |
Dog.prototype.bulk = CreateMethod({name: 'bulk', volume: 90});
поскольку TypeScript заявлен как "надмножество", а class - как "сахар", то старый добрый способ должен работать, наверно. и да - понятно, что ссылки на super в методе bulk не будет. |
Так можно, но нужно именно в стиле ES6
class Dog {
bulk: CreateMethod({name: 'bulk', volume: 90})
}
Например, здесь это делается с помощью интерфейса. Хотелось бы фабрику использовать |
Rise,
ну, это слишком жестко. Без eval'а нельзя разве? |
Всё какие-то запрещённые методы у вас) Это всё равно, что глубокое сравнение объектов делать, сравнивая их json-строки
|
В TS можно так сделать. Будет работать. Правда, когда применяю к классу декоратор, все такие методы пропадают, но это я как-то декоратор неправильно пишу
function CreateMethod(params) {
return function() {
console.log(params)
}
}
class Dog {
bulk = CreateMethod({name: 'bulk', volume: 90})
}
|
Цитата:
|
Цитата:
|
Никуда не пропадают, это я неправильную реализацию написал. Если делать так, то всё работает:
function logClass(target: any) {
// сохраняем ссылку на исходный конструктор
var original = target;
// вспомогательная функция для генерации экземпляров класса
function construct(constructor, args) {
var c : any = function () {
return constructor.apply(this, args);
}
c.prototype = constructor.prototype;
return new c();
}
// новое поведение конструктора
var f : any = function (...args) {
console.log("New: " + original.name);
return construct(original, args);
}
// копируем прототип, чтобы работал оператор instanceof
f.prototype = original.prototype;
// возвращаем новый конструктор (он переопределит исходный)
return f;
}
|
Пример декоратора, который я привел, не относится к задаче (т.е. он не участвует в определении методов). Просто я тестировал своё решение в т.ч. со сторонними декораторами, чтобы убедиться, что всё работает как надо.
К сожалению, мой способ не работает в ES6 и это привязывает меня к TS |
Цитата:
на каждый экземпляр - новый метод. При том что тут не нужны данные из экземпляра. если экземпляров много, таки лучше через прототип. Конечно, выглядит совсем не по-хипстерски, зато правильнее :) |
Учитывая, что это всё используется в Ангуляре (кто-то использует TS по-другому? ;-), а сервисы там преимущественно синглтоны, то копировать свойства в конструкторе нормально... Не понимаю, зачем ангуляровцы, вообще, ООП используют при таком раскладе
|
Что происходит, когда мы возвращаем значение из конструктора?
var f : any = function (...args) {
console.log("New: " + original.name);
return new original(...args);
}
В документации по ES6/TypeScript таких примеров нет |
Цитата:
|
А где об этом можно почитать? Это поведение ES6 или TypeScript добавляет?
|
Цитата:
https://developer.mozilla.org/ru/doc.../Operators/new это стандартное js-ное поведение, существует от сотворения мира. |
И как это я упустил этот момент) Спасибо!
|
Еще вопрос, чтобы новую тему не создавать. Пример из статьи: https://www.sitepen.com/blog/2015/10...pt-decorators/
Реализация декоратора @readonly
function readonly<TFunction extends Function>(Target: TFunction): TFunction {
let newConstructor = function () {
Target.apply(this);
Object.freeze(this);
};
newConstructor.prototype = Object.create(Target.prototype);
newConstructor.prototype.constructor = Target;
return <any> newConstructor;
}
Зачем здесь делают Target.apply(this);? По-моему, это просто вызовет конструктор без аргументов. Смысл? Ну и newConstructor.prototype = Object.create(Target.prototype); newConstructor.prototype.constructor = Target; так ли нужны? |
Цитата:
Target.apply(this, arguments); По сути, тут создается новый класс, который в точности как Target, только объекты после создания замораживаются. Потому конструктор вызывать надо. Цитата:
newConstructor.prototype = Target.prototype; |
Цитата:
|
Да.
|
Цитата:
newConstructor.prototype = Object.create(Target.prototype); newConstructor.prototype.constructor = newConstructor;быть может, это и подразумевалось, только автор опечатался |
Всё равно декоратор не работает нормально. Если делаю так, то всё ОК
function decorator(target) {
return target
}
@decorator
class MyClass {
public constructor( @Inject(HttpClient) protected http: HttpClient) {}
}
Но как только вместо target возвращаю в декораторе новый конструктор, DI перестает работать (сервисы не инжектятся) :( P.S. Сделал планк https://embed.plnkr.co/opnY3y/ |
Читал документацию по декораторам и смотрел как они в ES транспилятся - ничего сложного)
На stackoverflow ответили: https://stackoverflow.com/questions/...46606#45646606 Тоже пробовал что-то подобное, но оказывается нужно было newCtor делать именованной, а в конце все-таки использовать newConstructor.prototype = Object.create(target.prototype), а не newConstructor.prototype = target.prototype; Видимо, Ангуляр как-то по-особенному свои зависимости инжектит. Вот в его исходниках так и не разобрался - слишком муторный код |
| Часовой пояс GMT +3, время: 04:06. |