在 javscript 中 AOP 的使用会带来很多方便,在程序的结构上实现了解耦,代码逻辑更加清晰。使用 AOP 我们还可以对逻辑进行非侵入性的改造,下面来看下用法:
用于非侵入的插入逻辑
before 函数
1 2 3 4 5 6 7 8 9 10
| Function.prototype.before = function(func) { var __self = this; return function() { if (func.apply(this, arguments) == false) { return false; } return __self.apply(this, arguments); } }
|
after 函数
1 2 3 4 5 6 7 8 9 10 11
| Function.prototype.after = function(func) { var __self = this; return function() { var ret = __self.apply(this, arguments); if (ret === false) { return false; } func.apply(this, arguments); return ret; } }
|
考虑这种情形,我们需要在 onload 方法之后或者之前执行一些代码
比如,我们需要在 alert(1)之前 alert(0),在之后 alert(2)
1 2 3
| window.onload = function() { alert(1); }
|
我们考虑如下三种手段:
- 直接修改 onload 中的代码
1 2 3 4 5
| window.onload = function() { alert(0); alert(1); alert(2); }
|
无疑这种方式是耦合度最高,最具侵入性的方法。
2. 使用中间变量保存 onload
1 2 3 4 5 6 7 8 9 10 11 12 13
| window.onload = function() { alert(1); } var __onlaod = window.onload;
window.onload = function() { alert(0); if(__onlaod) { __onload(); } alert(1); }
|
这种方式较上一种好一些,但是这里需要维护一个中间变量,维护中间变量还是需要一些成本的,而且代码冗长不够优雅也不够通用。
3. 使用 AOP 的方式
1 2 3 4 5 6 7 8 9 10
| window.onload = function() { alert(1); }; window.onload = (window.onload || function(){}).before(function(){ alert(0); });
window.onload = (window.onload || function(){}).after(function(){ alert(2); });
|
使用 AOP 的方式显然是耦合度最低的非侵入式做法,代码也优雅很多,那我们来分析一下这两个函数的实现原理,以便今后我们可以自己写出这样的方法。
下面我们来逐行地分析一下这两个方法,首先来看 before
函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| Function.prototype.before = function(func) { var __self = this; //使用中间变量 __self 保存了 this 指针,此时的 this 指向调用者,也就是 onload 方法 return function() { //返回一个函数 if (func.apply(this, arguments) === false) { //使用 apply 函数用 this 指针调用 func,因为这时候这个函数已经被赋值给 window.onload,所以这时候的 this 指向的是 window 对象, //这里其实是 window.func(arguments); //如果返回值是非 false 的返回值,就继续执行下一步,这个 false 是应该在 before 函数中返回的,用于控制是否继续执行原始的 onload 方法。 return false; } return __self.apply(this, arguments); //如果返回值不是 flase,这里就直接使用 this 指向的 window 对象调用保存的 onload 方法,实现对原始 window.onload 的调用 } }
Function.prototype.after = function(func) { var __self = this; //使用中间变量 __self 保存了 this 指针,此时的 this 指向调用者,也就是 onload 方法 return function() { //返回一个函数 var ret = __self.apply(this, arguments); //直接使用 this 指针指向的 window 对象调用 onload 方法,实现 window 对 onload 方法的原始调用。并取方法调用的返回值。 if (ret === false) { //如果onload 方法中没有返回 false,则说明,onload 方法中允许执行后续的方法。继续执行后续步骤 return false; //否则就终止方法调用并且返回 false; } func.apply(this, arguments); //这时的 this 指针指向 window 对象,使用 window 对象调用 func //其实是 window.func(arguments); return ret; //返回返回值 } }
|
在本例中我们又一次看到了 ES 中 apply 的强大动态特性,很类似于 OC 中的 Swizzle
,动态的交换方法的实现,来实现非侵入的在方法的前后插入逻辑的动态装饰功能。
其实 AOP 实现的是一个拦截程序生命周期的方法,通过拦截程序的生命周期来对不同的功能实现解耦。它的实现依赖动态语言的运行时特性,通过使用这些特性来让代码变得更加优雅,可维护性更好。