从源码学习使用 node-delegates

栏目: JavaScript · 发布时间: 5年前

内容简介:安装和使用的代码在源码仓库都可以找到,这里主要先讲一下 API。用于创建一个 delegator 实例,用于把 proto 接收到的一些操作委托给它的 prop 属性进行处理。

node-delegatesTJ 大神所写的一个简单的小工具,源码只有 157 行,作用在于将外部对象接受到的操作委托到内部属性进行处理,也可以理解为讲对象的内部属性暴露到外部,简化我们所需要书写的代码。

安装和使用的代码在源码仓库都可以找到,这里主要先讲一下 API。

API

Delegate(proto, prop)

用于创建一个 delegator 实例,用于把 proto 接收到的一些操作委托给它的 prop 属性进行处理。

Delegate.auto(proto, targetProto, targetProp)

根据 targetProp 所包含的键,自动判断类型,把 targetProto 上的对应属性代理到 proto。可以是 getter、setter、value 或者 method。

Delegate.prototype.method(name)

在 proto 对象上新增一个名为 name 的函数,调用该函数相当于调用 proto 的 prop 属性上的 name 函数。

Delegate.prototype.getter(name)

新增一个 getter 到 proto 对象,访问该 getter 即可访问 proto 的 prop 的对应 getter。

Delegate.prototype.setter(name)

同 getter。

Delegate.prototype.access(name)

在 proto 上同时新增一个 getter 和一个 setter,指向 proto.prop 的对应属性。

Delegate.prototype.fluent(name)

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;
};

复制代码

以上所述就是小编给大家介绍的《从源码学习使用 node-delegates》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

ActionScript 3.0 Cookbook

ActionScript 3.0 Cookbook

Joey Lott、Darron Schall、Keith Peters / Adobe Dev Library / 2006-10-11 / GBP 28.50

Well before Ajax and Microsoft's Windows Presentation Foundation hit the scene, Macromedia offered the first method for building web pages with the responsiveness and functionality of desktop programs......一起来看看 《ActionScript 3.0 Cookbook》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

在线进制转换器
在线进制转换器

各进制数互转换器

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器