Skip to main content

· 4 min read
liaohuanyu

面向切面编程(AOP)简介

该文中一个重要的启发是利用函数替换的方式来实现AOP(当然这样并不是最好的,下面文章会说到),例子:

替换方法

var origDoSomething = thing.doSomething;
// Method replacement is a simple form of AOP
thing.doSomething = function() {
doSomethingElseFirst();
return origDoSomething.apply(this, arguments);
}

替换原型

var origDoSomething = Thing.prototype.doSomething;
Thing.prototype.doSomething = function() {
var start = Date.now();
var result = origDoSomething.apply(this, arguments);
console.log((Date.now() - start) + 'ms', x, y, result);
return result;
}

上面的方式也被称为 Monkey patching,这种方式通常用于 polyfill 浏览器已有的方法。

用AOP改善javascript代码

实现AOP可以用上文中的替换方式,但是这样会出现一些问题:

多了一个讨厌的中间变量比如origDoSomething, 来管理它也要花费一些额外的成本.

该文给出了两个典型的切面函数,利用这些切面函数结合各种设计模式可以实现无侵入式编程。

切面函数before

Function.prototype.before = function( func ){
var __self = this;
return function(){
if( func.apply( this, arguments ) === false ){
return false;
}
return __self.apply( this, arguments );
}
}

这样的函数调用的方法如下:

var a = function(){
console.log('1')
}

a.before( function(){
return false
} )()

原理分析:将需要增加功能的函数称为原函数,原函数这个对象调用原型链上的before方法,该方法接收一个函数作为参数func,这个函数会在原函数之前调用。before方法的原理是:将指向原函数对象的this保存到内部变量__self中,然后返回一个闭包函数,这个函数会先调用func,如果func返回的结果不满足某些条件就不再执行原函数,如果满足则在闭包所在上下文中执行原函数也就是__self指向的函数。

切面函数after

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

这里和before原理一样,只是会对原函数的执行结果进行拦截,通过某些判断逻辑来决定是否执行after注入的函数以及返回原函数结果。

小结

上面提到了两种实现 AOP 的方式,我们稍微总结一下适用场景:

  1. 第一种方法适用于你确实想修改第三方库某个函数的行为,无论是你主动调用该方法还是库本身或者其他的依赖也调用了该方法,你添加的额外操作都生效。比如 san-devtools 中实现时间旅行的方式。
  2. 第二种方法只会在你主动调用 类似 before 的 AOP 函数的时候会生效。

· 4 min read
liaohuanyu

面向切面编程(AOP)简介

该文中一个重要的启发是利用函数替换的方式来实现AOP(当然这样并不是最好的,下面文章会说到),例子:

替换方法

var origDoSomething = thing.doSomething;
// Method replacement is a simple form of AOP
thing.doSomething = function() {
doSomethingElseFirst();
return origDoSomething.apply(this, arguments);
}

替换原型

var origDoSomething = Thing.prototype.doSomething;
Thing.prototype.doSomething = function() {
var start = Date.now();
var result = origDoSomething.apply(this, arguments);
console.log((Date.now() - start) + 'ms', x, y, result);
return result;
}

上面的方式也被称为 Monkey patching,这种方式通常用于 polyfill 浏览器已有的方法。

用AOP改善javascript代码

实现AOP可以用上文中的替换方式,但是这样会出现一些问题:

多了一个讨厌的中间变量比如origDoSomething, 来管理它也要花费一些额外的成本.

该文给出了两个典型的切面函数,利用这些切面函数结合各种设计模式可以实现无侵入式编程。

切面函数before

Function.prototype.before = function( func ){
var __self = this;
return function(){
if( func.apply( this, arguments ) === false ){
return false;
}
return __self.apply( this, arguments );
}
}

这样的函数调用的方法如下:

var a = function(){
console.log('1')
}

a.before( function(){
return false
} )()

原理分析:将需要增加功能的函数称为原函数,原函数这个对象调用原型链上的before方法,该方法接收一个函数作为参数func,这个函数会在原函数之前调用。before方法的原理是:将指向原函数对象的this保存到内部变量__self中,然后返回一个闭包函数,这个函数会先调用func,如果func返回的结果不满足某些条件就不再执行原函数,如果满足则在闭包所在上下文中执行原函数也就是__self指向的函数。

切面函数after

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

这里和before原理一样,只是会对原函数的执行结果进行拦截,通过某些判断逻辑来决定是否执行after注入的函数以及返回原函数结果。

小结

上面提到了两种实现 AOP 的方式,我们稍微总结一下适用场景:

  1. 第一种方法适用于你确实想修改第三方库某个函数的行为,无论是你主动调用该方法还是库本身或者其他的依赖也调用了该方法,你添加的额外操作都生效。比如 san-devtools 中实现时间旅行的方式。
  2. 第二种方法只会在你主动调用 类似 before 的 AOP 函数的时候会生效。