JS中AOP的实现和运用
在编写js的时候,我们有时会遇到针对某种场景做处理,比如在方法开始的时候校验参数,执行方法前检查权限,或是删除前给出确认提示等等。这些校验方法、权限检测、确认提示,规则可能都是相同的,在每个方法前去调用,显得麻烦,而且不利于统一管理,于是我们想到了面向切面编程(AOP)。
1. 简单AOP实现
简单的AOP实现,就是在原函数执行的前后,增加运行before和after两个增强方法,用这个新函数替换原函数,为此,我编写了一个类似于构造器的函数(后文就称它为构造器),代码如下:
// originFun为原函数,before和after为增强方法
function constructor(originFun, before, after){
function _class(){
before.apply(this, arguments);
originFun.apply(this, arguments);
after.apply(this, arguments);
}
return _class;
}
使用时,用构造器生成的新函数替换原函数即可,测试代码如下:
// 加法运算,作为测试的原函数
function calcAdd(a,b){
console.log(a + "+" + b + "=" + (a + b));
return a+b;
}
// AOP增强
calcAdd = constructor(calcAdd, function(){console.log("我在原方法前执行")}, function(){console.log("我在原方法后执行")});
// 调用方法进行测试
calcAdd(2, 3);
打印在控制台的测试结果:
我在原方法前执行
2+3=5
我在原方法后执行
2. AOP工厂
在某些场景下,使用的增强方法是相同的,每次将增强方法作为参数传递有点麻烦,于是我做了一个工厂方法,把增强方法作为参数,这样就可以生成不同的构造器,在不同场景调用不同的构造器即可:
// AOP工厂
var aopFactory = function(before, after){ // 构造方法,在原方法前后增加执行方法
function constructor(originFun){
function _class(){
var result;
proxy.before(arguments);
result = originFun.apply(this, arguments);
proxy.after(arguments);
return result;
}
return _class;
} var proxy = {
// 添加被代理方法,参数a为被代理方法,参数b为目标对象
add : function(a, b){
var funName;
// 判断参数a类型,可以为方法或方法名
if(typeof a == "function"){
funName = a.name;
}else if(typeof a == "string"){
funName = a;
}else{
return;
}
// 不传对象时默认为window对象
b = b || window;
if(typeof b == "object" && b[funName]){
// 替换原方法
b[funName] = constructor(b[funName]);
}
},
// 默认before为空方法
before : function(){},
// 默认after为空方法
after : function(){}
} // 注入特定的前后处理方法
if(typeof before == "function"){
proxy.before = before;
}
if(typeof after == "function"){
proxy.after = after;
} return proxy;
}
测试代码如下:
var printProxy, checkProxy; // 打印参数
function printArguments(){
var i, length;
for(i=0, length=arguments.length; i<length; i++){
console.info("param" + (i + 1) + " = " + arguments[i]);
}
}
// 验证参数是否为数字
function checkNumber(){
var i, length;
for(i=0, length=arguments.length; i<length; i++){
if(typeof arguments[i] != "number")
console.error(arguments[i] + "不是数字");
}
} // 将printArguments方法作为前置通知,生成打印参数的构造器
printProxy = aopFactory(printArguments);
// 将checkNumber方法作为前置通知,生成验证参数是否为数字的构造器
checkProxy = aopFactory(checkNumber); // 加法
function calcAdd(a,b){
console.log(a + "+" + b + "=" + (a + b));
return a+b;
}
// 减法
function calcMinus(a,b){
console.log(a + "-" + b + "=" + (a - b));
return a-b;
}
// 乘法
function calcMultiply(a,b){
console.log(a + "*" + b + "=" + (a * b));
return a*b;
}
// 除法
function calcDivide(a,b){
console.log(a + "/" + b + "=" + (a / b));
return a/b;
} // 为加减法生成验证参数是否为数字的代理方法
checkProxy.add(calcAdd);
checkProxy.add(calcMinus);
// 为乘除法生成打印参数的代理方法
printProxy.add(calcMultiply);
printProxy.add(calcDivide); // 测试
calcAdd("4", 5);
calcMinus(6, "a");
calcMultiply(4, 5);
calcDivide(6, 3);
测试结果如下:
4不是数字
4+5=45
a不是数字
6-a=NaN
param1 = 4
param2 = 5
4*5=20
param1 = 6
param2 = 3
6/3=2
View Result
3. 进一步优化AOP工厂
在before方法中,验证到参数不正确,我们并不想让方法继续执行下去。我们可以让before方法返回一个布尔值,作为停止执行的标志。在原方法执行前检查before方法的返回值,判断是否继续往下执行。
另外,为每个方法生成代理都要调用一次add,这还不够简单,于是想到了正则表达式,通过循环,把所以满足正则表达式的方法都进行增强。
根据以上两点得到新AOP工厂方法:
// 优化后的AOP工厂
var aopFactory = function(before, after){ // 构造方法,在原方法前后增加执行方法
function constructor(originFun){
function _class(){
var result;
result = proxy.before.apply(this,arguments);
// 如果before方法返回false,则直接return不再往下执行
if(typeof result == "boolean" && !result){return;}
result = originFun.apply(this, arguments);
proxy.after.apply(this,arguments);
return result;
}
return _class;
} var proxy = {
// 添加被代理方法,参数a为被代理方法,参数b为目标对象
add : function(a, b){
var funName, index;
// 不传对象时默认为window对象
b = b || window;
if(typeof b != "object")
return;
// 判断参数a类型,如果为正则表达式
if(typeof a == "object" && a.test){
// 替换所以满足正则表达式的方法
for(index in b){
if(a.test(index)){
b[index] = constructor(b[index]);
}
}
return;
}
// 判断参数a类型,取出方法名
if(typeof a == "function"){
funName = a.name;
}else if(typeof a == "string"){
funName = a;
}else{
return;
}
// 如果方法存在,替换原方法
if(b[funName]){
b[funName] = constructor(b[funName]);
}
},
// 默认before为空方法
before : function(){},
// 默认after为空方法
after : function(){}
} // 注入特定的前后处理方法
if(typeof before == "function"){
proxy.before = before;
}
if(typeof after == "function"){
proxy.after = after;
} return proxy;
}
测试代码:
var checkProxy, myFunc; // 验证参数是否为数字,是数字就打印,否则给出错误提示
function checkNumber(){
var i, length, flag = true;
for(i=0, length=arguments.length; i<length; i++){
if(typeof arguments[i] != "number"){
console.error(arguments[i] + "不是数字");
flag = false;
}else{
console.info("param" + (i + 1) + " = " + arguments[i]);
}
}
return flag;
} // 将checkNumber方法作为前置通知,生成验证参数是否为数字的构造器
checkProxy = aopFactory(checkNumber); myFunc = {
// 加法
calcAdd : function(a,b){
console.log(a + "+" + b + "=" + (a + b));
return a+b;
},
// 减法
calcMinus : function (a,b){
console.log(a + "-" + b + "=" + (a - b));
return a-b;
},
// 计算幂
power : function(a,b){
console.log(a + "^" + b + "=" + Math.pow(a, b));
return Math.pow(a, b);
}
} // 对myFunc对象中所有"calc"开头的方法进行增强
checkProxy.add(/^(calc).*/, myFunc); // 测试
console.log("calcAdd--------------");
myFunc.calcAdd(4, 5);
console.log("calcMinus--------------");
myFunc.calcMinus(6, "a");
console.log("power--------------");
myFunc.power(4, 3);
测试结果:
calcAdd--------------
param1 = 4
param2 = 5
4+5=9
calcMinus--------------
param1 = 6
a不是数字
power--------------
4^3=64
View Result
测试对myFunc中所有"calc"开头的方法进行增强,从结果可以看到,加减法运算前都打印了参数,而且减法运算由于参数不正确,并没有执行。幂运算不是我们希望增强的方法,它的参数没有被打印出来。
对于增强方法中有回调函数的情况,我想到的是把原函数及其参数作为回调函数的参数进行传递,没有发现什么更好方法,就不细说了。
最后,文章中有什么问题,或是大家有什么好的想法,记得告诉我哦!
JS中AOP的实现和运用的更多相关文章
- js 中的方法注入(aop)
js 中的方法注入 java中很多框架支持 apo 的注入, js中也可以类似的进行实现 主要是通过扩展js中方法的老祖 Function 对象来进行实现. Function.prototype.af ...
- js 实现 aop
Aop又叫面向切面编程,用过spring的同学肯定对它非常熟悉,而在js中,AOP是一个被严重忽视的技术点,这篇就通过下面这几个小例子,来说说AOP在js中的妙用. 1, 防止window.onloa ...
- A2D JS框架 - AOP封装
AOP在js中的实现,先看看用法吧: var A2D = $.noConflict();//不要误会,此乃我自己写的A2D框架,非jQuery function fn1(name, age) { co ...
- JS中的高阶函数
JS中的高阶函数 高阶函数是指以函数作为参数的函数,并且可以将函数作为结果返回的函数. 1. 高阶函数 接受一个或多个函数作为输入 输出一个函数 至少满足以上一个条件的函数 在js的内置对象中同样存在 ...
- 5.0 JS中引用类型介绍
其实,在前面的"js的六大数据类型"文章中稍微说了一下引用类型.前面我们说到js中有六大数据类型(五种基本数据类型 + 一种引用类型).下面的章节中,我们将详细讲解引用类型. 1. ...
- 【repost】JS中的异常处理方法分享
我们在编写js过程中,难免会遇到一些代码错误问题,需要找出来,有些时候怕因为js问题导致用户体验差,这里给出一些解决方法 js容错语句,就是js出错也不提示错误(防止浏览器右下角有个黄色的三角符号,要 ...
- JS中给正则表达式加变量
前不久同事询问我js里面怎么给正则中添加变量的问题,遂写篇博客记录下. 一.字面量 其实当我们定义一个字符串,一个数组,一个对象等等的时候,我们习惯用字面量来定义,例如: var s = &quo ...
- js中几种实用的跨域方法原理详解(转)
今天研究js跨域问题的时候发现一篇好博,非常详细地讲解了js几种跨域方法的原理,特分享一下. 原博地址:http://www.cnblogs.com/2050/p/3191744.html 下面正文开 ...
- 关于js中的this
关于js中的this this是javascript中一个很特别的关键字,也是一种很复杂的机制,学习this的第一步就是要明白this既不指向函数自身也不指向函数的词法作用域,this实际上是函数被调 ...
随机推荐
- <1>Python生成高质量Html文件:Pyh模块+Bootstrap框架
一,介绍 QQ交流群:585499566 本文的目的是怎么使用Pyh+Bootstrap快速生成简约,大方,清新的Html页面,涉及到的技能:Python小白技能,Pyh会阅读中文文档,Html基础, ...
- (jQuery插件)autocomplete插件的简单例子
1.引入相应的js和css,我用到的时候是在jquery-ui的js里面整合的,ui的css 2.先在html上写一个input <input id="tags" class ...
- matlab练习程序(地图上画经纬度)
需要看下生成的数据在地球上的经纬度具体位置. 投影为墨卡托投影. clear all; close all; clc; load coast; a=load('out.txt'); %自己的经纬度 ...
- codeforces 803D Magazine Ad(二分+贪心)
Magazine Ad 题目链接:http://codeforces.com/contest/803/problem/D ——每天在线,欢迎留言谈论. 题目大意: 给你一个数字k,和一行字符 例: g ...
- MongoDB 安装与配置
MongoDB下载 官方下载链接:https://www.mongodb.com/download-center/community MongoDB安装 简单,按提示安装即可.安装方式: 1. Com ...
- spark RDD,reduceByKey vs groupByKey
Spark中有两个类似的api,分别是reduceByKey和groupByKey.这两个的功能类似,但底层实现却有些不同,那么为什么要这样设计呢?我们来从源码的角度分析一下. 先看两者的调用顺序(都 ...
- 智能POS正餐主副机模式FAQ(无桌台或桌台模块)
1.无桌台 (1).如果是初次使用,首先检查是否是新建的机器号,新建的机器是默认关闭桌台的,需要到模块管理中开启. 2.无桌台模块 (1).是否在主副机开启连接上后重启主机与副机,且同步数据.
- mssql sqlserver两条求和sql脚本相加的方法分享
转自:http://www.maomao365.com/?p=7205 摘要: 下文分享两条sql求和脚本,再次求和的方法分享 /* 例: 下文已知两条sql求和脚本,现需对两张不同表的求和记录再次求 ...
- Spring入门详细教程(一)
一.spring概述 Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用.Spring是于2003 年兴起的一个轻量级的 ...
- 第二章 基础查询 2-1 SQL语句基础
一.列的查询 基本的SELECT语句: SELECT <列名 >,...... FROM < 表名>; 注:子句是SQL的组成要素. 注:查询结果中的列的顺序和SELECT子句 ...