Javascript 使用基于实例的装饰器发布订户模式';这';总是未定义的

Javascript 使用基于实例的装饰器发布订户模式';这';总是未定义的,javascript,decorator,Javascript,Decorator,如您所见,每次调用subscribe函数时,都会有人调用concreteObserver.publish()但是,当您调用observatable.subscribe(target[propertyKey])然后“this”变为未定义 我还尝试重写描述符getter并调用该getter,但仍然没有定义。在类中,我可以通过调用target.prototype.functionName来包装函数 当我知道将调用什么函数名时,这就起作用了,但是@Subscribe的函数名可以是任意的,因此我不能在类级

如您所见,每次调用
subscribe
函数时,都会有人调用
concreteObserver.publish()
但是,当您调用
observatable.subscribe(target[propertyKey])然后“this”变为未定义

我还尝试重写描述符getter并调用该getter,但仍然没有定义。在类中,我可以通过调用
target.prototype.functionName
来包装函数

当我知道将调用什么函数名时,这就起作用了,但是
@Subscribe
的函数名可以是任意的,因此我不能在类级装饰器上使用它,除非我使用反射来检测类的所有注释


编辑

到目前为止已经试过了

observable.subscribe(目标[propertyKey].bind(this))

返回undefined,在这种情况下,subscribe具有正确的上下文

observable.subscribe(数据=>descriptor.value.apply(这个,数据))还未定义“this”

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
     }
 }

我想出的解决办法。由于只能在类装饰器中获取类的实例,因此可以正确使用
this
,在subscribe函数中,我告诉我应该订阅哪个函数,然后在ClassDecorator中,我遍历每个方法,以确定它们的原型中是否有
\uu subscribebeffunction
,从而在绑定
实例时订阅该方法

descriptor.value = function(){
   console.log(this); //undefined 
}

descriptor.get = function(){
   console.log(this); //undefined
}

这不起作用,因为在构造类本身时,但在创建任何实例之前,会调用decorator。因为没有实例,所以不能有
this
–您只能访问原型,但类属性不在原型上(与方法不同)

您可以使用以下示例验证这一点:

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
     }
 }
这就产生了输出

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");

也就是说,您可以编写另一个应用于(比如)类级别的decorator来代理构造函数。如果
@Subscribe
注释在原型上存储了一些元数据,那么类装饰器就可以查找它并进行实际的连接。所以得到了

Before declaring class
@Example initialized
@Example called
After declaring class
Before creating instance
After creating instance
Before calling method
After calling method

工作应该是可能的。事实上,您甚至可以通过代理
@Subscribe
装饰器中的构造函数来摆脱第二个装饰器,但是您仍然需要存储元数据,以便在实例化过程中查看
不提供任何
上下文。您希望看到什么,
可观察的
实例,
MyClass
实例,或者其他什么?处理程序正确地设置了它,但是它的subscribeToValue未定义,即使在绑定它时,您究竟在何处以及如何绑定它?在decorator内调用subscribe似乎是个问题,但我必须解决这个问题,这是我担心必须采用的解决方案,然后我的类注释基本上变成了方法的实例工厂annotations@Pavlo实际上,也可以从方法级修饰符(
target.constructor
)更改构造函数的描述符。我还没试过,但值得一试。这将为您节省第二个装饰程序,但不会节省整个查找元数据过程。但是使用反射元数据应该不难;target.constructor.prototype[propertyKey]=函数(数据:any){console.log(数据);console.log(this);//未定义的原始(数据).bind(this);};可观察。订阅(目标[propertyKey]);//subscribe(target.constructor.prototype[propertyKey])不起作用是的,那不起作用。我的意思是你基本上可以做类装饰器必须从方法装饰器中做的事情。不过,它仍然需要存储一些元数据。覆盖构造函数的示例如下:
@AutoSubscribe()
class MyClass {
  @Subscribe(observer)
  subscribe(value) {
    console.log(this.x);
  }
}