这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」
本文为 koa 依赖系列文章,前几篇也可以在站内查看:
- koa 中依赖的库 parseurl
- Koa 依赖的库 type-is 和 content-disposition
- Koa 依赖的库 accepts、content-type 和 cache-content-type
- Koa 依赖的库 encodeurl 和 escape-html
- Koa 中依赖的库 statuses
- Koa 依赖的库 cookies
- Koa 依赖的库 on-finished 和 destroy
- Koa 依赖的库 http-errors 和 http-assert
- Koa 依赖的库 fresh 和 vary
Koa 的 application、context、request、response 都重写了 toJSON 方法,因此我们在序列化事只会看到部分属性,这里是使用 only 库来实现的,以 request 为例:
toJSON () {
return only(this, [
'method',
'url',
'header'
])
}
这里 only 做的事情就是从对象上取部分属性组成新的对象,这个功能很容易,因此实现也很简单:
module.exports = function(obj, keys){
obj = obj || {};
if ('string' == typeof keys) keys = keys.split(/ +/);
return keys.reduce(function(ret, key){
if (null == obj[key]) return ret;
ret[key] = obj[key];
return ret;
}, {});
};
以上是 only 的全部源码,内部只是使用了一个 reduce,这里参数可以传递数组也可以传递空格分隔的字符串,字符串也会被处理为数组。only 的原理和使用都很简单,接下来再看一个很简单的库。
delegates 是用来实现委托能力的库,在 koa 中,我们可以使用 ctx.url 来访问 ctx.request.url,我们也可以通过 ctx.redirect 来调用 ctx.response.redirect,类似这种调用方式就是通过 delegates 实现的。
delegates 库导出一个 Delegator 的构造函数,它接收一个原型 proto 和一个属性名 target 作为参数,上面有 auto、method、access、getter、setter、fluent 多个方法,分别用来处理不同的委托类型:
- method 的处理最简单,直接使用 apply 调用方法即可。
- getter 和 setter 是调用 proto 上的
__defineGetter__和__defineSetter__实现的。 - access 即 getter + setter,实际上源码也是先调用 getter 再调用 setter。
- auto 可以接收其他 proto 为参数,因此实际上就是对上面的属性进行遍历之后分别执行 method、getter 和 setter。
- fluent 是返回一个函数,这个函数不带参数时为 getter,带参数时为 setter,内部是通过定义新的函数实现的。
Delegator.prototype.fluent = function (name) {
var proto = this.proto;
var target = this.target;
this.fluents.push(name);
proto[name] = function(val){
if ('undefined' != typeof val) {
this[target][name] = val;
return this;
} else {
return this[target][name];
}
};
return this;
};
method、access、getter、setter、fluent 每个方法都会返回 this,因此这些方法都支持链式调用。
在 Delegator 对象中有 method、getter、setter、fluent 四个数组,数组里面是已经添加的委托属性名,可以用来做查询使用:
function Delegator(proto, target) {
if (!(this instanceof Delegator)) return new Delegator(proto, target);
this.proto = proto;
this.target = target;
this.methods = [];
this.getters = [];
this.setters = [];
this.fluents = [];
}