【问题标题】:Why is `this` undefined when passing a Function into Metadata and how to fix it?为什么将函数传递给元数据时“this”未定义以及如何修复它?
【发布时间】:2020-09-22 02:48:23
【问题描述】:

我想在 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");

上面代码sn-p的输出是

hello
undefined

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


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

yarn install
yarn start

【问题讨论】:

  • 你试过.bind()吗? myMethod() { ... }.bind(this)
  • @Ifaruki 我想过这个问题。但我不确定在我的示例中如何做到这一点,因为我在MethodDecorator 中没有对this 的引用。也许在target的帮助下?
  • 嗯,你只是把 this.num =4; 放在你的构造函数中吗?
  • @Ifaruki 这只是一个例子。在我的真实用例中,有在运行时分配的值
  • 我不完全理解你的问题,但我知道这一点:你可以用这样的自定义调用函数:myfunc.call(this) 或者,我认为更有可能的解决方案是 ES6箭头函数实际上修改了我们习惯的 this 关键字。尝试使用普通函数而不是箭头函数。

标签: typescript reflect-metadata


【解决方案1】:

如果您通过将this 的值传递给other.init 来保存this 的值,如下所示,然后将该值绑定到每个方法,它将起作用。不幸的是,not seem possible 可以将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");

【讨论】:

  • 这当然不漂亮,但它有效!如果有人提出更好的方法,我会等几天 :) 谢谢
【解决方案2】:

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

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

【讨论】:

  • 听起来很合理。但我认为还有其他问题(我已经尝试过您的解决方案,this 仍然未定义)
猜你喜欢
  • 1970-01-01
  • 2014-06-12
  • 1970-01-01
  • 2016-12-11
  • 2018-07-31
  • 1970-01-01
  • 1970-01-01
  • 2017-10-27
  • 2016-02-06
相关资源
最近更新 更多