成龙大哥突然来问我一个前端问题,他问:

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

听到这个问题的时候,我是一头雾水的,不是用 this 就好了吗。

然后他就给了个场景,让我来怼。

场景伪代码

照着大哥的想法,我撸了一个伪代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
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()

锁定毒瘤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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值和若干个指定的参数值的前提下调用某个函数或方法。

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

.apply

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

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

.bind

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

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

1
2
3
4
5
request()
.then((v) => this.check.bind(this)(v))

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

不用 this

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

1
2
3
4
5
6
7
static check(value) {
if (!value.isCert) {
Fetch.get();
} else {
console.log("Get it!");
}
}

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

修改后的代码

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();

除非注明,麦麦小家文章均为原创,转载请以链接形式标明本文地址。

版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

本文地址:https://blog.micblo.com/2017/03/17/ES6-%E4%B8%8B%E5%AE%9E%E7%8E%B0%E9%9D%99%E6%80%81%E6%96%B9%E6%B3%95%E9%97%B4%E7%9A%84%E7%9B%B8%E4%BA%92%E8%B0%83%E7%94%A8/