首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用基于实例的装饰器“this”发布订阅者模式总是未定义的

使用基于实例的装饰器“this”发布订阅者模式总是未定义的
EN

Stack Overflow用户
提问于 2019-01-05 16:05:47
回答 1查看 94关注 0票数 0
代码语言:javascript
复制
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));

如果返回未定义的内容,则订阅在本例中具有正确的上下文。

observable.subscribe(data => descriptor.value.apply(this, data));也有未定义的“this”

代码语言:javascript
复制
descriptor.value = function(){
   console.log(this); //undefined 
}

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

我想出的解决办法。由于只能在类装饰器中获取类的实例,所以可以在那里正确地使用this,在订阅函数中,我告诉应该订阅哪个函数,然后在ClassDecorator中迭代每个方法,以确定它们在原型中是否有__subscribeFunction,从而在绑定instance时订阅该方法。

代码语言:javascript
复制
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
     }
 }
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-01-06 15:02:33

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

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

代码语言:javascript
复制
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");

它产生输出

代码语言:javascript
复制
Before declaring class
@Example initialized
@Example called
After declaring class
Before creating instance
After creating instance
Before calling method
After calling method

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

代码语言:javascript
复制
@AutoSubscribe()
class MyClass {
  @Subscribe(observer)
  subscribe(value) {
    console.log(this.x);
  }
}

工作应该是可能的。实际上,您甚至可以通过从@Subscribe装饰器中代理构造函数来摆脱第二个装饰器,但是仍然需要存储元数据,以便在实例化过程中查看。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54053728

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档