js 支撑 Aspect 切面编程

系列文章:读 arale 源码之 class 篇

运用 Aspect,能够许可你在指定要领实行的前后插进去特定函数

before object.before(methodName, callback, [context])

在 object[methodName] 要领实行前,先实行 callback 函数.

callback 函数在实行时,吸收的参数和传给 object[methodName] 雷同。

dialog.before('show', function(a, b) {
    a; // 1
    b; // 2
});

dialog.show(1, 2)

after object.after(methodName, callback, [context])

在 object[methodName] 要领实行后,再实行 callback 函数.

callback 函数在实行时,吸收的参数第一个是 object[methodName] 的返回值,以后的参数和传给 object[methodName] 雷同。

dialog.after('show', function(returned, a, b) {
  returned; // show 的返回值
    a; // 1
    b; // 2
});

dialog.show(1, 2);

源码

定义两个出口

exports.before = function(methodName, callback, context) {
    return weave.call(this, "before", methodName, callback, context);
};

exports.after = function(methodName, callback, context) {
    return weave.call(this, "after", methodName, callback, context);
};

定义一个可柯里化的函数 weave,柯里化成 after、before 函数

function weave(when, methodName, callback, context) {
    var names = methodName.split(/\s+/);
    var name, method;
    while (name = names.shift()) {
      // this 指向基于 Base 天生的类的实例,如上例的 dialog
    method = getMethod(this, name);
    // 假如该函数(show)是第一次切面化绑定,则包装该函数。
    // 被包装的函数在实行前后都邑触发下面注册的事宜。
    if (!method.__isAspected) {
      wrap.call(this, name);
    }
    // 注册事宜:比方 after:show 、 before:show .
    this.on(when + ":" + name, callback, context);
  }
  return this;
}

假如没有在实例中找到该要领(show),则抛出非常。

// 在实例中查找该要领,若没有则抛出非常
function getMethod(host, methodName) {
    var method = host[methodName];
    if (!method) {
    throw new Error('Invalid method name: ' + methodName);
  }
  return method;
}

定义一个包装器函数,被包装的函数在实行前后(before、after)都邑触发一个特定的函数

// 包装器,使该函数(show)支撑切面化
function wrap(methodName) {
  // 保留旧的要领,this 指向该对象(dialog)
    var old = this[methodName];
    // 定义新的要领,并在旧要领之前触发 before 绑定的函数,以后触发 after 绑定的函数
    this[methodName] = function() {
    var args = Array.prototype.slice.call(arguments);
    var beforeArgs = ["before:" + methodName].concat(args);
    // 触发 before 绑定的函数,假如返回 false 则阻挠原函数 (show) 实行
    if (this.trigger.apply(this, beforeArgs) === false) return;
    // 实行旧的函数,并将返回值看成参数传递给 after 函数
    var ret = old.apply(this, arguments);
    var afterArgs = ["after:" + methodName, ret].concat(args);
    // 触发 after 绑定的函数,绑定的函数的第一个参数是旧函数的返回值
    this.trigger.apply(this, afterArgs);
    return ret;
  }
  // 包装以后打个标记,不必再反复包装了
  this[methodName].__isAspected = true;
}
    原文作者:honger05
    原文地址: https://segmentfault.com/a/1190000004057804
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞