为什么将函数传递到元数据时未定义“this”以及如何修复它?
Why is `this` undefined when passing a Function into Metadata and how to fix it?
我想借助 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
未定义,我该如何解决?
您可以通过克隆 this 示例存储库和 运行
自己尝试
yarn install
yarn start
那是因为你在这一行 return 使用 function
而不是箭头函数:
return function MethodDecorator(
这会创建一个新的上下文(函数作用域),这会导致 this
改为指向全局对象。 this
已定义:未定义的是 this.num
,因为 window.num
不存在。
因此,如果您 return 将函数作为箭头函数,词法 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
的值传递给 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");
我想借助 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
未定义,我该如何解决?
您可以通过克隆 this 示例存储库和 运行
自己尝试yarn install
yarn start
那是因为你在这一行 return 使用 function
而不是箭头函数:
return function MethodDecorator(
这会创建一个新的上下文(函数作用域),这会导致 this
改为指向全局对象。 this
已定义:未定义的是 this.num
,因为 window.num
不存在。
因此,如果您 return 将函数作为箭头函数,词法 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
的值传递给 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");