View on GitHub

为食喵

Cat for food - A project for restaurant order

ES6下实现静态方法间的相互调用

问题描述

小程序开发的时候总是会遇到一些坑,好比说我遇到的一个情况:

使用 ES6 的类,类中有两个方法彼此会相互调用,那怎么办?

本来想着,不是用 this 就好了吗。

但是其实是有问题的!伪代码如下:

class Fetch{
  static 检查返值() {
    检查;
    如果签名过期,get()
  }
  static get() {
    请求
    .then(检查返回值)
    
  }
}

Fetch.get();

可以看到就一个类 Fetch,里面有两个静态方法——检查返值get

类的外面只有一行代码——Fetch.get(),用于调用 Fetch 的静态方法 get

先从 Fetch 的静态方法 get 入手,我们可以看到 get 方法内只有一行代码,里面的 then 暴露了“请求”是一个 Promise 的实例。

可以发现,“请求”接收到 resolved 信号时,需要执行 Fetch 的静态方法 检查返值

在检查反值中,程序系那个会判定“请求”resolve 时带上的参数。如果里面的参数不符合规定,则重新进行一次 Fetch.get()

锁定问题

class Fetch{
  static check(value) {
    if (!value.isCert) {
      this.get();
    } else {
      console.log("Get it!");
    }
  }
  static get() {
    request()
      .then(this.check)
  }
}

// Start to fetch
Fetch.get();

大哥的代码有毒,问题发生在 request().then() 上。

大哥的做法是 request().then(this.check)

虽然 check 方法还是可以执行,但是它只能执行一次。因为 Promisethen 内的作用域默认是全局 (global/windows),而且在严格模式下是 undefined,因此,this 指向(call)的不是 Fetch。所以在执行 this.check 函数时,check 内的 this 并不是 Fetch 对象,this.get() 取出来的是 undefined,故 this.get(); 是无法执行的,达不到大哥的需求。

动手修改

既然 this.checkcall 的不是 Fetch 本身,那么我们可以用 .call.apply.bind等可以调整函数的 this 的方式实现作用域的传递。

.call

我们知道,call() 方法在使用一个指定的this值和若干个指定的参数值的前提下调用某个函数或方法。

request()
    .then((v) => this.check.call(this, v))

.apply

类似于 .call ,只是它传递参数值靠数组。

request()
    .then((v) => this.check.apply(this, [v]))

.bind

bind() 方法会创建一个新函数。

当这个新函数被调用时,bind()的第一个参数将作为它运行时的 this, 之后的一序列参数将会在传递的实参前传入作为它的参数。

request()
    .then((v) => this.check.bind(this)(v))
    
request()
    .then((v) => this.check.bind(this, v)())

不用 this

不用 this 就相当于直接指向 Fetch 本身。

static check(value) {
    if (!value.isCert) {
        Fetch.get();
    } else {
        console.log("Get it!");
    }
  }

但是这样的做法,不是很好,可维护性比较差。

修改后的代码

最后,我们选择了最简洁的 .call 来实现这个需求。

function request() {
  return new Promise(function (resolve, reject) {
    setTimeout(() => resolve(Math.round(Math.random() * 5)), 500)
  });
}

class Fetch{
  static cnt = 1;
  static check(value) {
    if (value) {
      console.log(`Fail!`);
      this.get();
    } else {
      console.log("Get it!");
    }
  }
  static get() {
    console.log(`Get now! count: ${this.cnt++}`);
    request()
      .then((v) => this.check.call(this, v)); //here
  }
}

// Start to fetch
Fetch.get();