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.defineProperty、Object.defineProperty 或 Proxy 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 实现,改进修改属性描述符的实现方法等等,也算对旧知识点的一次回顾。