Arale源码剖析(3)——Base模块和Aspect模块

本文同步自我的GitHub博客

媒介

Base这个模块实际上才是Arale模块体系中对外的模块,它包括了之前引见的Class类和Events类,以及本身内部的attribute模块和aspect模块,因而Base模块是真正的基本类。

因为Attribute模块的内容太多,而Aspect模块和它关联也不太大,因而,考虑到文章篇幅的均衡,将Base模块的剖析分红两篇,Attribute模块的剖析放在下一篇零丁来写。

带解释源码

Base源码的开首是如许的:

var Class = require('arale-class');
var Events = require('arale-events');
var Aspect = require('./aspect');
var Attribute = require('./attribute');

可见,全部Base的完成是基于上面这四个模块的,前两个模块已剖析过了,下面来剖析背面两个模块。首先是Aspect模块,这个模块实际上只供应了两个要领beforeafter

// `before`和`after`实际上是对`weave`要领的一次封装,供应易用的接口
// 在指定要领实行前,先实行 callback
exports.before = function(methodName, callback, context) {
  return weave.call(this, 'before', methodName, callback, context);
};

// 在指定要领实行后,再实行 callback
exports.after = function(methodName, callback, context) {
  return weave.call(this, 'after', methodName, callback, context);
};

// Helpers
// -------
// 事宜支解
var eventSplitter = /\s+/;

/**
 * 掌握callback的实行时序
 * @param  {String}   when       挑选是`before`照样`after`
 * @param  {String}   methodName 要领名字符串
 * @param  {Function} callback   回调函数
 * @param  {Object}   context    上下文对象
 * @return {Object}              挪用此要领的对象
 */
function weave(when, methodName, callback, context) {
  // 获得要领名数组
  var names = methodName.split(eventSplitter);
  var name, method;

  // 遍历要领名数组
  while (name = names.shift()) {
    // 获得要领函数
    method = getMethod(this, name);
    // 要领是不是被革新过,假如没有则举行革新
    if (!method.__isAspected) {
      wrap.call(this, name);
    }
    // 绑定一下事宜
    this.on(when + ':' + name, callback, context);
  }

  return this;
}

/**
 * 获得对应称号的要领
 * @param  {Object} host       挪用对象
 * @param  {String} methodName 要领称号
 * @return {Function}            要领函数
 */
function getMethod(host, methodName) {
  // 获得对象上对应的要领函数
  var method = host[methodName];
  // 假如要领不存在则报错
  if (!method) {
    throw new Error('Invalid method name: ' + methodName);
  }
  return method;
}

/**
 * [wrap description]
 * @param  {[type]} methodName [description]
 * @return {[type]}            [description]
 */
function wrap(methodName) {
  // 获得对象上的要领
  var old = this[methodName];

  // 对要领举行革新封装
  // 革新过的要领实行时,会先触发'before:methodName'事宜
  this[methodName] = function() {
    // 切分参数
    var args = Array.prototype.slice.call(arguments);
    // 在参数数组前增加一项'before:methodName'
    var beforeArgs = ['before:' + methodName].concat(args);

    // prevent if trigger return false
    // 先触发`before:methodName`事宜,假如存在回调函数行列且实行后返回false,则阻挠进一步往下实行
    if (this.trigger.apply(this, beforeArgs) === false) return;

    // 实行原要领,保留返回值
    var ret = old.apply(this, arguments);

    // 组织参数数组,实行`after:methodName`事宜
    var afterArgs = ['after:' + methodName, ret].concat(args);
    this.trigger.apply(this, afterArgs);

    return ret;
  };

  // 修正要领是不是被革新状况属性
  this[methodName].__isAspected = true;
}

然后是Base模块,它集成了Event, AspectAttribute模块的种种属性,实际上是Arale类库的一个进口模块:

var Class = require('arale-class');
var Events = require('arale-events');
var Aspect = require('./aspect');
var Attribute = require('./attribute');


module.exports = Class.create({
  // 混入Events, Aspect, Attribute模块的一切属性
  Implements: [Events, Aspect, Attribute],

  // 一切用`Base.extend()`构建的类在初始化时都邑挪用的要领
  initialize: function(config) {
    this.initAttrs(config);

    // 将`this._onChangeAttr`注册为`change:attr`事宜的监听函数
    parseEventsFromInstance(this, this.attrs);
  },

  destroy: function() {
    // 卸载一切事宜监听
    this.off();

    // 消灭一切属性
    for (var p in this) {
      if (this.hasOwnProperty(p)) {
        delete this[p];
      }
    }

    // destroy一次后this都被消灭了,再次挪用会报错,因而天生一个空的destroy,该要领与主同在
    this.destroy = function() {};
  }
});

/**
 * 将`_onChangeAttr`要领注册为`change:attr`事宜的监听函数
 * @param  {Class} host  挪用对象
 * @param  {Object} attrs 包括一切要注册属性的对象
 */
function parseEventsFromInstance(host, attrs) {
  for (var attr in attrs) {
    if (attrs.hasOwnProperty(attr)) { // 检测attr是attrs的非继续属性
      var m = '_onChange' + ucfirst(attr);
      if (host[m]) {
        host.on('change:' + attr, host[m]);
      }
    }
  }
}

/**
 * 将首字母转变为大写
 * @param  {String} str 要处置惩罚的字符串
 * @return {String}     处置惩罚完的字符串
 */
function ucfirst(str) {
  return str.charAt(0).toUpperCase() + str.substring(1);
}

源码剖析

Aspect

Aspect模块就是完成了两个要领,beforeafter。这两个要领的作用就是针对类上的某个要领,给这个要领绑定先于其实行和后于其实行的回调函数。

两个要领实际上挪用的都是同一个要领weave,只是将before和after作为参数传入,在weaver要领中,对要举行beforeafter“伪事宜”绑定的要领举行查找,找到后会检测这个要领上是不是有__isAspected属性。这个属性的作用是标示出此要领有无被举行过伪事宜的“包装”。

上一段一连提到两次“伪事宜”这个词,它是我编出来的,示意的意义为before:methodNameafter:methodName如许的事宜并不能成为一个自力的事宜,而是依附于methodName这个原要领的。本来的事宜实行流程是如许的。

event.trigger(eventName)  +------------+
------------------------->| someMethod |----------->被触发实行
                          +------------+

一旦在someMethod上注册了afterbefore事宜后,someMethod就会被封装成一个新的函数:

someMethod被封装后天生的新wrappedMethod:
                                      |trigger()
                  +-------------------------------------------------------+
                  |wrappedMethod:     |触发`before:method`事宜             |
                  |                   |                                   |
                  |            +---------------+  return false +-----+    |
                  |            |  beforeMethod |-------------->| end |    |
                  |            +---------------+               +-----+    |
                  |                   |return true                        |
                  |                   |                                   |
                  |            +---------------+                          |
                  |            |    method     |                          |
                  |            +---------------+                          |
                  |                   |触发`after:method`事宜              |
                  |                   |                                   |
                  |            +---------------+                          |
                  |            |  afterMethod  |                          |
                  |            +---------------+                          |
                  +-------------------------------------------------------+

全部模块的症结就在于wrap这个用来封装要领的函数了,固然完成这一功用的也须要功用完整的Event模块的支撑。

    原文作者:classicemi
    原文地址: https://segmentfault.com/a/1190000000777645
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞