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 })
學習了委託模式,能夠把外部對象接收到的操做委託給內部屬性(或其餘對象)進行具體的處理。