aopjs
为Nodejs设计的AOP实现,也可以用于其他AMD JavaScript 环境。
可以用来做什么?
- 日志或认证/授权等常见AOP用途
- 当你想改变所依赖的npm包的功能时,可以使用 aopjs 在代码层面 “切换” 它们的某些函数,变量,常量,而不必硬修改它们的源代码,从而避免依赖的包在升级后造成的版本冲突。
安装
$ npm i aopjs
测试
$ npm i aopjs -d
$ make test
./node_modules/.bin/mocha \
--reporter list \
--timeout 1000 \
-u tdd \
test/*.js
․ AOP # AOP.createCallableInstance: 5ms
․ AOP # AOP.compile.fundef: 20ms
․ AOP # AOP.createAdvisor: 2ms
․ AOP # AOP.for.function: 42ms
․ AOP # AOP.for.const: 37ms
․ joinpoints # JointFunctionDefine.match(): 14ms
․ joinpoints # JointVariable.match(): 6ms
․ joinpoints # JointConst.match(): 4ms
․ PathPattern # PathPattern.match: 5ms
․ util # util.localScopeByPos(): 6ms
10 passing (149ms)
调试
$ npm i aopjs -d
$ make debug
将打开一个支持断点和inspecte的nodejs内置的cl界面,然后自动执行aopjs所有的测试脚本。
如果你对aopjs的某些功能有疑问,
make debug
是个不错的调试方式,Fork this repository, fix it, and send me a request pull :)
快速开始
这是一个简单的例子,b.js 文件对 a.js 文件进行aop操作:找到a.js里名为 foo
的函数,切入一个 around
类型的函数,在 around 函数里替换传递给 foo 的参数
a.js
// this is a.jsexports{ return "hello " + who ;}
b.js
// this is b.jsvar aop = ; // <-- 找到了 a.js 文件里的 foo 方法 // 切入一个 around 类型的函数,由 around 函数调用原始函数 ; var foo = foo ;console ;
执行结果会打印:
hello you
概念
AOP = Aspect Oriented Programming (面向切面编程)
-
将系统里的代码定义为连接点(
joinpoint
/joint
),aopjs 目前支持的joinpoint
仅为:函数定义(before/around/after),常量(getter),变量(setter/getter) -
按特性找到连接点(
joinpoint
),将有用的joinpoint
集合在一起,形成切入点(pointcut
) -
对切入点(
pointcut
)进行函数切入(cut
)操作,被切入到pointcut
上的函数称为“方面”(adive
);aopjs目前实现的advice类型有:before,around,after,setter,getter
API
文件路径模板
aopjs用文件路径模板
来匹配目标文件:
-
绝对路径:
var aop = ;; -
相对路径:
;或
; -
使用
“*”
做为通配符:// 匹配folder目录下的所有 .js 后缀文件(不包括子目录下的文件!); -
使用
“**”
做为多级目录通配符:// 匹配folder及其子目录下的所有.js文件; -
使用数组来传递多个文件路径模板:
;
连接点 Joinpoint
函数 defun(namePattern)
-
#### 函数名称 ####
namePattern 可以是一个字符串或正则表达式
// a.js 文件中名为 foo 的函数 aop("/some/folder/a.js").defun("foo") ;
或
// folder目录下所有.js文件中,所有名称以 foo(忽略大小写) 开头的函数 aop("/some/folder/*.js").defun(/^foo/i) ;
只有关键词
function
后的部分被当做函数名称,将函数赋值给变量,变量名不会作为函数名称,例如:var foo = function (){ ... }
无法用foo找到函数 -
#### 函数空间(scope) ####
函数内部定义的函数需要先定位外层函数,例如:
{{{{// ... ...}}}}找到最里面的
qux
需要像这样:;任何类型的 连接点(joinpoint)都需要在
scope
里定位;每个函数都是一个scope
,aop()返回的是目标文件顶层scope,因此,通常都是从 aop() 开始。也许以后我会写一个类似 jQuery 的 selector 来定位连接点...
-
#### 匿名函数 ####
匿名函数也能被找到,用字符串
#n
表示匿名函数,n 从 0 开始计数;每个scope
都是独立计数的。// #0 not foovar {// #0var bar = {return "bar" ;} ;// #1var baz = {return "baz" ;} ;}// #1 not quxvar {// #0 (重新从0开始计数)var quux = {return "quux" ;} ;}定位上面文件中的3个内部匿名函数:
var joint_foo = ;// 第1个全局函数里的 第1个匿名函数joint_foo ;// 第1个全局函数里的 第2个匿名函数joint_foo ;// 第2个全局函数里的匿名函数;
普通常量 const(value[,position])
const()
用于在函数或顶层scope中定位代码里的常量。
-
value
可以是:string, int, float 类型的值,或者匹配字符串常量的正则表达式 -
position
表示匹配value
的常量出现的位置,从0计数;缺省表示所有匹配的常量
{ return 314125 ;} var foo = "hello" ;var bar = "hello world" ;
// 仅仅匹配 "hello" ; // 匹配字符串 "hello" 和 "hello world" ; // 只匹配字符串 "hello world" ; // 匹配 PI函数的返回值浮点数 ;