每天一个阅读一个 npm 包之 node-delegates

398 阅读2分钟

node-delegates

在 koa 的源码中看到了 TJ 的node-delegates 这个包,在此学习做个记录。

先看看这个包测试用例

node-delegates 一共提供了六个测试用例, 下面主要分析三个有代表性的用例。

  • 第一个用例
var assert = require('assert');
var delegate = require('../index.js');

describe('.method(name)', function(){
  it('should delegate methods', function(){
    var obj = {};

    obj.request = {
      foo: function(bar){
        return bar;
      }
    };

    delegate(obj, 'request').method('foo');

    obj.foo('something').should.equal('something');
  })
})

声明了一个 obj 对象, obj 对象又挂载了一个名字为 request 的对象, 这个对象上有一个名称为 foo 的函数,返回输入的参数。 delegate 函数接收了两个参数,第一个参数是 obj 对象, 第二个参数是一个字符串 request,链式调用一个函数 method, 参数为 'foo' 。 那么调用方法就直接使用 obj.foo 的方式调用。

  • 第二个用例
describe('.getter(name)', function(){
  it('should delegate getters', function(){
    var obj = {};

    obj.request = {
      get type() {
        return 'text/html';
      }
    }

    delegate(obj, 'request').getter('type');

    obj.type.should.equal('text/html');
  })
})

obj 对象下的 type 属性返回 'text/html',经过 delegate 函数 可以通过 obj.type 直接调用。

  • 第三个用例
describe('.auto(proto, targetProto, target)', function(){
  it('should apply properties', function(){
    var obj = {
      settings: {
        env: 'development'
      }
    };

    var setAs = 0;

    Object.defineProperty(obj.settings, 'getter', {
      get: function(){
        return this.env;
      }
    });
    Object.defineProperty(obj.settings, 'setter', {
      set: val => setAs = val
    });
    Object.defineProperty(obj.settings, 'constant', { value: 2 });

    delegate.auto(obj, obj.settings, 'settings');

    obj.env.should.equal('development');
    obj.getter.should.equal('development');
    obj.setter = 10;
    setAs.should.equal(10);
    obj.constant = 5;
    obj.constant.should.equal(2);
  })
})

将 obj.settings 下的 getters setters values method 委托到 obj 上。

从这三个测试用例我们可以很容易明白这个包是具体干什么的,一个词 委托!

源码分析

node-delegate 的具体实现不算复杂,加上注释一共 157 行。

  • 构造函数
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 = []
}
  • method 方法

这个方法没什么好说的,原型链上挂载 method 函数,把 target 上的某个属性挂也载到 proto 上。

Delegator.prototype.method = function (name) {
  var proto = this.proto
  var target = this.target
  this.methods.push(name)

  proto[name] = function() {
    return this[target][name].apply(this[target], arguments)
  }
}
  • getter 方法

这个方法中使用 Object.defineGetter 方法来重写 proto 上 name 属性的 getter 属性。

这个方法目前已经被废弃,可以使用 Reflect.definePropertyObject.definePropertyProxy handler 来写 proto 的 getter 属性。

Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  proto.__defineGetter__(name, function(){
    return this[target][name];
  });

  return this;
};

使用 Object.defineGetter 重写,Object.defineProperty 类似。

Delegator.prototype.getter = function (name) {
  var proto = this.proto
  var target = this.target
  this.getters.push(name)
  
  Reflect.defineProperty(proto, name, {
    return this[target][name]
  })  

  return this
}

使用

  • fluent 方法
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
}

这个包自从 TJ 宣布退出 NodeJS 后就不维护了,有空的话我们也可以进行一番改造,比如改为使用class 实现,改进修改属性描述符的实现方法等等,也算对旧知识点的一次回顾。