Koa 依赖的库 only 和 delegates

215 阅读2分钟

这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战

本文为 koa 依赖系列文章,前几篇也可以在站内查看:

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 = [];
}