前言

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模块,这个模块实际上只提供了两个方法before和after:

// `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, Aspect和Attribute模块的各种属性,实际上是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模块就是实现了两个方法,before和after。这两个方法的作用就是针对类上的某个方法,给这个方法绑定先于其执行和后于其执行的回调函数。

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

上一段连续提到两次“伪事件”这个词,它是我编出来的,表示的意思为before:methodName和after:methodName这样的事件并不能成为一个独立的事件,而是依附于methodName这个原方法的。原来的事件执行流程是这样的。

event.trigger(eventName) +------------+

------------------------->| someMethod |----------->被触发执行

+------------+

一旦在someMethod上注册了after或before事件后,someMethod就会被封装成一个新的函数:

someMethod被封装后生成的新wrappedMethod:

|trigger()

+-------------------------------------------------------+

|wrappedMethod: |触发`before:method`事件 |

| | |

| +---------------+ return false +-----+ |

| | beforeMethod |-------------->| end | |

| +---------------+ +-----+ |

| |return true |

| | |

| +---------------+ |

| | method | |

| +---------------+ |

| |触发`after:method`事件 |

| | |

| +---------------+ |

| | afterMethod | |

| +---------------+ |

+-------------------------------------------------------+

整个模块的关键就在于wrap这个用来封装方法的函数了,当然实现这一功能的也需要功能完备的Event模块的支持。

php ucfirst,Arale源码解析(3)——Base模块和Aspect模块相关推荐

  1. adb android源码分析,Android Adb 源码解析(base on Android 9.0)

    Adb 框架 Adb架构 Android Adb 一共分为三个部分:adb.adb server.adbd,源码路径:system⁩/⁨core⁩/⁨adb. adb和adb server 是运行在P ...

  2. dataset__getitem___PyTorch源码解析与实践(1):数据加载Dataset,Sampler与DataLoader

    献给学习PyTorch在路上或者计划较深入理解PyTorch的同行者们 写在前面 笔者一直使用tf,大势所趋决定转PyTorch,这个系列就作为我学习PyTorch的笔记与心得. 网络上PyTorch ...

  3. libev源码解析——定时器监视器和组织形式

    我们先看下定时器监视器的数据结构.(转载请指明出于breaksoftware的csdn博客) /* invoked after a specific time, repeatable (based o ...

  4. libev源码解析——监视器(watcher)结构和组织形式

    在<libev源码解析--总览>中,我们介绍了libev的一些重要变量在不同编译参数下的定义位置.由于这些变量在多线程下没有同步问题,所以我们将问题简化,所提到的变量都是线程内部独有的,不 ...

  5. 注册中心 Eureka 源码解析 —— 应用实例注册发现(五)之过期

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-evict/ ...

  6. Android View体系(六)从源码解析Activity的构成

    前言 本来这篇是要讲View的工作流程的,View的工作流程主要指的measure.layout.draw这三大流程,在讲到这三大流程之前我们有必要要先了解下Activity的构成,所以就有了这篇文章 ...

  7. Android之EasyPermissions源码解析

    转载请标明出处:[顾林海的博客] 个人开发的微信小程序,目前功能是书籍推荐,后续会完善一些新功能,希望大家多多支持! 前言 我们知道在Android中想要申请权限就需要在AndroidManifest ...

  8. unordered_map源码解析和个人理解

    参考链接 https://blog.csdn.net/ddkxddkx/article/details/6555754 还有这个源码解析https://zrj.me/archives/1248 1.下 ...

  9. Heritrix 3.1.0 源码解析(八)

    本文接着分析存储CrawlURI curi的队列容器,最重要的是BdbWorkQueue类及BdbMultipleWorkQueues类 BdbWorkQueue类继承自抽象类WorkQueue,抽象 ...

  10. The Wide and Deep Learning Model(译文+Tensorlfow源码解析) 原创 2017年11月03日 22:14:47 标签: 深度学习 / 谷歌 / tensorf

    The Wide and Deep Learning Model(译文+Tensorlfow源码解析) 原创 2017年11月03日 22:14:47 标签: 深度学习 / 谷歌 / tensorfl ...

最新文章

  1. LeetCode简单题之在区间范围内统计奇数数目
  2. Python图片操作-psd_tools:将psd文件转换成BMP
  3. 《C语言及程序设计》实践参考——分离整数和小数部分
  4. PyCharm中目录directory与包package的区别
  5. 未发先侃?对比华为,高通第二代5G调制解调器如何?
  6. DSP与STM32区别
  7. Azkaban业务流程如何转化为DataWorks业务流程
  8. PHP给后台管理系统加安全防护机制的一些方案
  9. 【MongoDB --番外】错误集合
  10. mssql mysql数据库大小_mssql查看数据库大小
  11. 用汇编的眼光看C++(之模板类)
  12. 相机模型-Extended Unified Camera Model
  13. mysql常用数据操作之增、删、改
  14. C盘\用户目录下\管理员文件夹 如何重命名?
  15. 数学建模美赛该如何准备?
  16. [Python] 开发一个局域网联机小游戏
  17. NGUI-动画Tween
  18. eclipse鼠标变成十字架
  19. 《剑指offer》刷题笔记(发散思维能力):求1+2+3+...+n
  20. 管理者的人品的重要性

热门文章

  1. linux双系统默认进入win10,win10 linux 双系统 默认win10启动
  2. 新能源汽车技术与市场
  3. PS如何删除智能图层为可编辑状态
  4. 快速 二进制,八进制,十进制,十二进制转换 .源码,反码,补码,
  5. 使用Python对比两个excel表格中的重复数据
  6. 《编程机制探析》第二章 计算机语言
  7. python批量修改图片大小_Python3 批量修改JPG图片尺寸?
  8. 修复WordPress中的Cookie阻止错误
  9. 手机浏览器一键跳转微信加好友和公众号关注的方法
  10. 王源就抽烟致歉 | 大数据分析禁烟对烟草行业的影响有多大