共计 3464 个字符,预计需要花费 9 分钟才能阅读完成。
JS 中面向切面编程 (AOP) 利用
什么是面向切面编程
面向切面编程(Aspect-Oriented Programming,AOP)是一种编程范式,是在面向对象编程(OOP)的根底上的一种补充。AOP 提供了一种灵便的、易于保护和跨多个对象和组件的切面(Aspect)的形式,能够在不批改原有代码的状况下实现代码的特定性能。
上面是一个简略的 JS 示例代码,实现了 AOP 的日志记录性能:
// 定义一个用于包装函数的高阶函数
function withLogging(fn) {return function() {console.log(`entering ${fn.name}`);
const result = fn.apply(this, arguments);
console.log(`exiting ${fn.name}`);
return result;
}
}
// 定义一个须要包装的函数
function add(x, y) {return x + y;}
// 将函数用 withLogging 包装
const loggedAdd = withLogging(add);
// 调用通过包装的函数
loggedAdd(2, 3);
在下面的代码中,withLogging 是一个高阶函数(Higher Order Function),它返回的函数能够包装一个函数,并可能记录该函数的输出和输入。add 是一个须要包装的函数,它的性能是相加两个数。用 withLogging 包装 add 失去的 loggedAdd 函数,能够在 add 函数执行前输入 ”entering add”,在 add 函数执行后输入 ”exiting add”,并返回 add 函数的执行后果。
扩大优化
在 JavaScript 中实现函数调用前和调用后执行某个办法的形式,能够应用 AOP(面向切面编程)的思维。具体实现形式是,应用一个函数对指标函数进行包装,而后在原始函数调用前或调用后进行相干操作。
以下是实现 before
和 after
函数并绑定到函数原型上的示例代码:
Function.prototype.before = function(beforeFn) {
const self = this; // 保留原函数的援用
return function() { // 返回蕴含原函数和前置函数的新函数
beforeFn.apply(this, arguments);
return self.apply(this, arguments);
};
};
Function.prototype.after = function(afterFn) {
const self = this; // 保留原函数的援用
return function() { // 返回蕴含原函数和后置函数的新函数
const result = self.apply(this, arguments);
afterFn.apply(this, arguments);
return result;
};
};
在上述代码中,咱们先给 Function
原型增加了两个办法:before
和 after
,这两个办法都承受一个函数作为参数。
before
函数会返回一个新函数,这个新函数会先调用传入的 beforeFn
函数,而后再调用原始函数,并将原始函数的返回值返回进来。
after
函数同样会返回一个新函数,这个新函数会先调用原始函数,而后再调用传入的 afterFn
函数,并将原始函数的返回值返回进来。
接下来,咱们能够以一个示例函数为例,演示如何应用 before
和 after
函数:
function greet(name) {console.log(`Hello, ${name}`);
}
const newGreet = greet.before(() => {console.log('before greet');
}).after(() => {console.log('after greet');
});
newGreet('Better');
// 执行后果:// before greet
// Hello, Better
// after greet
在上述代码中,咱们先定义了一个简略的 greet
函数,而后应用 before
和 after
函数,对其进行包装。咱们先应用 before
函数传入一个函数,输入一个 “before greet” 的提示信息;再应用 after
函数传入一个函数,输入一个 “after greet” 的提示信息。最初,在 newGreet
函数中调用 greet
函数,并将 'Better'
作为参数传入。
执行 newGreet('Better')
后,会依照以下程序输入三个提示信息:
before greet
Hello, Better
after greet
从输入后果能够看出,在 greet
函数执行前后,咱们都胜利地增加了一些额定的操作,这就体现了 AOP 的劣势。
终极计划
JavaScript 中的装璜器是一种非凡的函数,它能够用来批改类或对象的行为。装璜器能够在不批改原始类或对象定义的状况下,动静地增加、删除或批改它们的属性和办法。
装璜器能够被用来解决很多问题,比方减少验证、日志记录、性能剖析等性能,这些性能能够通过在类或对象的定义上利用不同的装璜器来实现。应用装璜器能够让代码具备更好的可维护性和可扩展性。
通常状况下,装璜器能够在函数、类、类成员上利用。
上面是一个应用装璜器来实现日志记录的例子:
// 定义一个装璜器,它接管一个指标对象和一个办法名
function log(target, name, descriptor) {
const original = descriptor.value; // 取出原有的办法
if (typeof original === 'function') { // 如果它的确是一个函数
descriptor.value = function (...args) { // 用新的函数代替原有的办法
console.log(`Calling ${name} with`, args); // 记录日志
const result = original.apply(this, args); // 调用原有的办法,并拿到它的返回值
console.log(`Result is`, result); // 记录返回值
return result; // 将返回值传递给调用者
};
}
return descriptor; // 返回批改后的办法描述符
}
// 定义一个类,其中有一个须要记录日志的办法
class Calculator {
@log
add(a, b) {return a + b;}
}
// 创立一个新的计算器实例,并调用它的 add 办法
const calculator = new Calculator();
const result = calculator.add(2, 3);
console.log(`Result is`, result);
在下面的示例代码中,咱们定义了一个装璜器 log
,用来给指标办法 add
增加日志性能。装璜器函数接管三个参数,别离是指标对象、办法名和描述符。咱们在装璜器中批改了 add
办法的描述符,用一个新的函数来代替原有的办法,并在新的函数中增加了日志记录的性能。
接着,咱们定义了一个 Calculator
类,其中有一个 add
办法。通过在 add
办法后面应用 @log
语法糖,咱们让 add
办法在被调用时主动增加日志记录性能。
最初,咱们创立了一个 Calculator
实例,并调用 add
办法进行运算。咱们能够在控制台中看到残缺的日志记录信息。这样,咱们就胜利地给办法增加了日志记录的性能,在不影响原有代码的前提下,对代码进行了扩大。
留神:目前 JavaScript 中类装璜器的反对仍处于第 3 阶段提案中(stage 3 proposal)。然而,咱们能够借助 Babel 和 TypeScript 编译器应用 JavaScript 装璜器。
AOP 的应用场景次要有以下几点:
- 日志记录:在办法执行前后打印调用日志,不便排查谬误。
- 权限管制:对办法进行拦挡,判断用户是否有执行该办法的权限。
- 缓存:对于须要大量计算的办法,增加缓存性能,来缩小计算次数。
AOP 的长处:
- 模块化:AOP 能够将整个应用程序分为多个模块,不便错误处理和批改。
- 可复用性高:切面能够逾越多个模块的代码进行批改,使得批改变得更加容易。
- 易于保护:切面能够帮忙程序员更加清晰地看到代码的执行流程,升高保护老本。
AOP 的毛病:
- 减少了代码复杂度:AOP 通过减少切面来实现其性能,会减少代码的复杂度。
- 运行效率:AOP 应用代理来实现切面,会对程序的运行效率产生肯定的负面影响。
总之,AOP 在实现某些性能时能够进步代码可复用性和可维护性,但在应用时须要衡量其具体的优缺点。
本文由 mdnice 多平台公布