问题描述
写了一个类,结果外部调用其 query
方法,报错:
排查发现,query 中无法使用 this.
class ModuleSelector {
public optionsRef: Ref;
public queryParams: ModuleListReq;
constructor() {
this.optionsRef = ref([]);
this.queryParams = {
page: 1,
pageSize: 20,
name: "",
value: "",
};
}
query = async (name: string = "") => {
console.log("query " + name);
console.log(this);
this.queryParams.name = name;
const ret = await system.module.list(this.queryParams);
this.optionsRef.value = UiHelper.converter.ListToSelectOptions(
ret.data.items
);
};
}
解决方法
调用方绑定 this
网上大多让调用方绑定:
function jump() {
console.log(this.name)
}
const obj = {
name: ' 米粒 ',
jump
}
jump = jump.bind(obj)
iframe.onload = function () {
this.frameLoaded();
}.bind(this);
问题在于调用方是外部库。
实现方绑定 this
在构造函数中:
this.frameLoaded.bind(this);
更好的办法?
自动绑定
这样每个函数都要绑定。很麻烦。因此首先想到用类似反射的方法绑定:Bind this for all methods in ES6 classes (github.com)
'use strict';
class Binder {}
Binder.getAllMethods = function(instance, cls) {
return Object.getOwnPropertyNames(Object.getPrototypeOf(instance))
.filter(name => {
let method = instance[name];
return !(!(method instanceof Function) || method === cls);
});
}
Binder.bind = function(instance, cls) {
getAllMethods(instance, cls)
.forEach(mtd => {
instance[mtd] = instance[mtd].bind(instance);
})
}
module.exports = Binder;
甚至有人专门写了个库:sindresorhus/auto-bind: Automatically bind methods to their class instance (github.com)
更改声明方式(箭头函数)
在默认情况下,类函数中的 this 其实指向的是执行环境
我讨厌引入三方库,所以转换思路,函数就是对象,所以不如直接这样声明:
query = async (name: string = "") => {
// ...
};
解决。原理:箭头函数的 this 向上继承。
this 永远指向一个对象?
粗略来说定义了 this,确实会指向对象。但其实不代表它不能是 undefined
,就如我们所遇到的。