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”变得不确定。
我还尝试重写描述符getter并调用该方法,但是我仍然不确定。在类上,我能够通过调用target.prototype.functionName
来包装一个函数。
当我知道要调用的函数名称是什么,但@Subscribe
的函数名称可以是任意的时,此方法有效,因此除非在我使用反射来检测所有类的注释。
编辑
到目前为止尝试过
observable.subscribe(target[propertyKey].bind(this));
返回未定义的值,在这种情况下,subscribe具有正确的上下文。
observable.subscribe(data => descriptor.value.apply(this, data));
的“ this”也未定义
descriptor.value = function(){
console.log(this); //undefined
}
descriptor.get = function(){
console.log(this); //undefined
}
我想出了解决方案。由于只能在类装饰器中获取类的实例,因此可以正确使用this
,因此在subscription函数中,我告诉我应该订阅哪个函数,然后在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
}
}
答案 0 :(得分:1)
这不起作用,因为装饰器是在构造类本身时但在创建任何实例之前调用的。由于没有实例,因此不会有this
–您只能访问原型,但类属性不在原型上(与方法不同)。
您可以使用以下示例对此进行验证:
function Example() {
console.log("@Example initialized");
return function exampleDescriptior(target, propertyKey, descriptor) {
console.log("@Example called");
}
}
console.log("Before declaring class");
class Test {
@Example()
public test() {}
}
console.log("After declaring class");
console.log("Before creating instance");
const test = new Test();
console.log("After creating instance");
console.log("Before calling method");
test.test();
console.log("After calling method");
产生输出
Before declaring class
@Example initialized
@Example called
After declaring class
Before creating instance
After creating instance
Before calling method
After calling method
也就是说,您可以 做的是编写另一个装饰器,该装饰器应用在例如类级别的代理构造器上。如果您的@Subscribe
注释在原型上存储了一些元数据,则类装饰器可以查找它并进行实际连接。所以得到类似
@AutoSubscribe()
class MyClass {
@Subscribe(observer)
subscribe(value) {
console.log(this.x);
}
}
上班应该是可能的。实际上,您甚至可以通过代理@Subscribe
装饰器中的构造函数来摆脱第二个装饰器,但是您仍然必须存储在实例化过程中可以浏览的元数据。