2

我想在 Typescript 装饰器和reflect-metadata的帮助下创建一个抽象。但是当我调用传递给元数据的函数时,this它是未定义的:

import "reflect-metadata";

const METHODS = "__methods__";
const Method = (obj: object) => {
  return function MethodDecorator(
    target: object,
    methodName: string | symbol,
    descriptor: PropertyDescriptor
  ) {
    const metadata = Reflect.getMetadata(METHODS, obj);

    Reflect.defineMetadata(
      METHODS,
      { ...metadata, [methodName]: descriptor.value },
      obj
    );
  };
};

const someObject: object = new Object();
class Main {
  private num = 42;

  constructor(other: Other) {
    other.init(someObject);
  }

  @Method(someObject)
  myMethod() {
    console.log("hello");
    console.log(this.num); // this is undefined (how can I fix it?)
  }
}

class Other {
  private methods: Record<string, Function> = {};

  init(obj: object) {
    this.methods = Reflect.getMetadata(METHODS, obj);
  }

  trigger(methodName: string) {
    this.methods[methodName]();
  }
}

const other = new Other();
new Main(other);
other.trigger("myMethod");

上面代码片段的输出是

hello
undefined

为什么this未定义,我该如何解决?


您可以通过克隆示例 repo 并运行来自己尝试

yarn install
yarn start
4

2 回答 2

3

如果您将 的值this传递给other.init,如下所示,然后将该值绑定到每个方法,它将起作用。不幸的是,它似乎不可能直接传递this给装饰器,尽管这样会更干净。

const someObject: object = new Object();
class Main {
  private num = 42;

  constructor(other: Other) {
    other.init(someObject, this);
  }

  @Method(someObject)
  myMethod() {
    console.log("hello");
    console.log(this.num); // 42
  }
}

class Other {
  private methods: Record<string, Function> = {};

  init(obj: object, thisArg: object) {
    this.methods = Reflect.getMetadata(METHODS, obj);
    Object.keys(this.methods).forEach((method) => {
      this.methods[method] = this.methods[method].bind(thisArg);
    })
  }

  trigger(methodName: string) {
    this.methods[methodName]();
  }
}

const other = new Other();
new Main(other);
other.trigger("myMethod");
于 2020-06-05T23:13:43.043 回答
0

那是因为您function在这一行返回 a 而不是箭头函数:

return function MethodDecorator(

这会创建一个新的上下文(函数范围),这会导致this指向全局对象。this已定义:它this.num是未定义的,因为window.num不存在。

因此,如果您将该函数作为箭头函数返回,则词法this将被保留,您将可以访问this.num

const Method = (obj: object) => {
  return (
    target: object,
    methodName: string | symbol,
    descriptor: PropertyDescriptor
  ) => {
    const metadata = Reflect.getMetadata(METHODS, obj);

    Reflect.defineMetadata(
      METHODS,
      { ...metadata, [methodName]: descriptor.value },
      obj
    );
  };
};
于 2020-06-03T11:59:19.363 回答