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