问题描述

写了一个类,结果外部调用其 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,就如我们所遇到的。