class Observable {
constructor() {
this.handlers = [];
}
publish(value) {
this.handlers.forEach(handler => {
handler(value);
});
}
subscribe(callback) {
this.handlers.push(callback);
}
}
const concreteObserver = new Observable();
function Subscribe(observable) {
return function functionDescriptor(target, propertyKey, descriptor) {
observable.subscribe(target[propertyKey]);
return descriptor;
}
}
class MyClass {
constructor(){
this.x = 5;
}
@Subscribe(concreteObserver)
subsribeToValue(value) {
console.log(this.x); // undefined
}
}
Как видите, функция subscribe
вызывается каждый раз, когда кто-то вызывает concreteObserver.publish()
, однако, когда вы вызываете observable.subscribe(target[propertyKey]);
, тогда this становится неопределенным.
Я также попытался переопределить метод получения дескриптора и вызвать его, но я все еще не определен. На занятиях я смог обернуть функцию, вызвав target.prototype.functionName
.
Это работает, когда я знаю, как будет называться имя функции, но имя функции для @Subscribe
может быть произвольным, поэтому я не могу использовать его в декораторе уровня класса, если я не использую Reflection для обнаружения всех аннотаций класс.
EDIT
Пока пробовал
observable.subscribe(target[propertyKey].bind(this));
, которая возвращает неопределенное значение, в этом случае подписка имеет правильный контекст.
observable.subscribe(data => descriptor.value.apply(this, data));
также имеет 'this' как неопределенное
descriptor.value = function(){
console.log(this); //undefined
}
descriptor.get = function(){
console.log(this); //undefined
}
Решение, которое я придумал. Поскольку получить экземпляр класса можно только в декораторе классов, тогда именно здесь this
может использоваться должным образом, в функции подписки я сообщаю, на какую функцию мне следует подписаться, затем в ClassDecorator я выполняю итерацию каждый метод, чтобы определить, имеют ли они __subscribeFunction
в своем прототипе и, таким образом, подписаться на метод при связывании instance
class Observable {
constructor() {
this.handlers = [];
}
publish(value) {
this.handlers.forEach(handler => {
handler(value);
});
}
subscribe(callback) {
this.handlers.push(callback);
}
}
const concreteObserver = new Observable();
function ClassDecorator(target) {
const originalTarget = target;
const Override = function (...args) {
const instance = originalTarget.apply(this, args);
Object.values(instance.__proto__).forEach(method => {
const observableFunction = method.prototype.__subscribeFunction;
if (observableFunction) {
observableFunction.subscribe(method.bind(instance));
}
});
return instance;
};
Override.prototype = originalTarget.prototype;
customElements.define(elementName, target);
return Override;
}
function Subscribe(observable) {
return function functionDescriptor(target, propertyKey, descriptor) {
target[propertyKey].prototype.__subscribeFunction = observable;
}
}
@ClassDecorator
class MyClass {
constructor(){
this.x = 5;
}
@Subscribe(concreteObserver)
subsribeToValue(value) {
console.log(this.x); // 5
}
}