node-delegates 是 TJ 大神所写的一个简单的小工具,源码只有 157 行,做用在于将外部对象接受到的操做委托到内部属性进行处理,也能够理解为讲对象的内部属性暴露到外部,简化咱们所须要书写的代码。node
安装和使用的代码在源码仓库均可以找到,这里主要先讲一下 API。git
用于建立一个 delegator 实例,用于把 proto 接收到的一些操做委托给它的 prop 属性进行处理。github
根据 targetProp 所包含的键,自动判断类型,把 targetProto 上的对应属性代理到 proto。能够是 getter、setter、value 或者 method。api
在 proto 对象上新增一个名为 name
的函数,调用该函数至关于调用 proto 的 prop 属性上的 name 函数。app
新增一个 getter 到 proto 对象,访问该 getter 便可访问 proto 的 prop 的对应 getter。koa
同 getter。函数
在 proto 上同时新增一个 getter 和一个 setter,指向 proto.prop 的对应属性。工具
access
的特殊形式。学习
delegate(proto, 'request') .fluent('query') // getter var q = request.query(); // setter (chainable) request .query({ a: 1 }) .query({ b: 2 });
/** * Expose `Delegator`. */ // 暴露 Delegator 构造函数 module.exports = Delegator; /** * Initialize a delegator. * 构造一个 delegator 实例 * @param {Object} proto 外部对象,供外部调用 * @param {String} target 外部对象的某个属性,包含具体处理逻辑 * @api public */ function Delegator(proto, target) { // 若是没有使用 new 操做符调用构造函数,则使用 new 构造 if (!(this instanceof Delegator)) return new Delegator(proto, target); // 构造实例属性 this.proto = proto; this.target = target; this.methods = []; this.getters = []; this.setters = []; this.fluents = []; } /** * Automatically delegate properties * from a target prototype * 根据 targetProp 自动委托,绑定一个属性到 Delegator 构造函数 * @param {Object} proto 接受请求的外部对象 * @param {object} targetProto 处理具体逻辑的内部对象 * @param {String} targetProp 包含要委托的属性的对象 * @api public */ Delegator.auto = function(proto, targetProto, targetProp){ var delegator = Delegator(proto, targetProp); // 根据 targetProp 获取要委托的属性 var properties = Object.getOwnPropertyNames(targetProto); // 遍历全部要委托的属性 for (var i = 0; i < properties.length; i++) { var property = properties[i]; // 获取 targetProto 上对应属性的 descriptor var descriptor = Object.getOwnPropertyDescriptor(targetProto, property); // 若是当前属性的 get 被重写过,就做为 getter 委托(使用 __defineGetter__ 或者 Object.defineProperty 指定 getter 都会重写 descriptor 的 get 属性) if (descriptor.get) { delegator.getter(property); } // 同 get,若是 set 被重写过,那就做为 setter 委托 if (descriptor.set) { delegator.setter(property); } // 若是当前 property 具备 value,那么判断是函数仍是普通值 if (descriptor.hasOwnProperty('value')) { // could be undefined but writable var value = descriptor.value; if (value instanceof Function) { // 是函数就进行函数委托 delegator.method(property); } else { // 是普通值就做为 getter 委托 delegator.getter(property); } // 若是这个值能够重写,那么继续进行 setter 委托 if (descriptor.writable) { delegator.setter(property); } } } }; /** * Delegate method `name`. * * @param {String} name * @return {Delegator} self * @api public */ Delegator.prototype.method = function(name){ var proto = this.proto; var target = this.target; this.methods.push(name); // 在 proto 上定义一个 name 的方法 proto[name] = function(){ // 实际仍是调用的 proto[target][name],内部的 this 仍是指向 proto[target] return this[target][name].apply(this[target], arguments); }; return this; }; /** * Delegator accessor `name`. * * @param {String} name * @return {Delegator} self * @api public */ Delegator.prototype.access = function(name){ // 同时定义 getter 和 setter return this.getter(name).setter(name); }; /** * Delegator getter `name`. * 委托 name getter * @param {String} name * @return {Delegator} self * @api public */ Delegator.prototype.getter = function(name){ var proto = this.proto; var target = this.target; this.getters.push(name); // 使用 __defineGetter__ 绑定 name getter 到 proto proto.__defineGetter__(name, function(){ // 注意 this 指向 proto 自己,因此 proto[name] 最终访问的仍是 proto[target][name] return this[target][name]; }); // 此处 this 指向 delegator 实例,构造链式调用 return this; }; /** * Delegator setter `name`. * 在 proto 上委托一个 name setter * @param {String} name * @return {Delegator} self * @api public */ Delegator.prototype.setter = function(name){ var proto = this.proto; var target = this.target; this.setters.push(name); // 经过 __defineSetter__ 方法指定一个 setter 到 proto proto.__defineSetter__(name, function(val){ // 注意 this 指向 proto 自己,因此对 proto[name] 设置值即为为 proto[target][name] 设置值 return this[target][name] = val; }); // 返回自身实现链式调用 return this; }; /** * Delegator fluent accessor * * @param {String} name * @return {Delegator} self * @api public */ Delegator.prototype.fluent = function (name) { var proto = this.proto; var target = this.target; this.fluents.push(name); proto[name] = function(val){ // 若是 val 不为空,那么就做为 setter 使用 if ('undefined' != typeof val) { this[target][name] = val; // 完过后返回 proto 自身,实现链式调用 return this; } else { // 若是 val 未定义,那么做为 getter 使用,返回具体的值 return this[target][name]; } }; return this; };
之因此会研究一下这个库是由于在看 koa
源码的时候看到使用了这个库,在 koa
中经过使用 node-delegates
把 context.request
和 context.response
上的属性都委托到了 context
自身。因此咱们能够直接使用 context.query
、context.status
来进行操做,简化了咱们所写的代码。this
koa 源码位置连接:https://github.com/koajs/koa/blob/b7fc526ea49894f366153bd32997e02568c0b8a6/lib/context.js#L191
经过 __defineGetter__
和 __defineSetter__
能够设置 getter 和 setter,可是 MDN 显示这两个 API 已被 deprecated,github 也已经有人提了 issue 和 pr。另外,经过这两个 API 设置 getter 和 setter 时,传递的函数的内部 this 指向原来的属性,好比:
let a = { nickName: 'HotDog' } a.__defineGetter__('name', function() { return this.nickName // 此处 this 仍然指向 a })
学习了委托模式,能够把外部对象接收到的操做委托给内部属性(或其余对象)进行具体的处理。