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(面向切面编程)的思维。具体实现形式是,应用一个函数对指标函数进行包装,而后在原始函数调用前或调用后进行相干操作。

以下是实现 beforeafter 函数并绑定到函数原型上的示例代码:

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 原型增加了两个办法:beforeafter,这两个办法都承受一个函数作为参数。

before 函数会返回一个新函数,这个新函数会先调用传入的 beforeFn 函数,而后再调用原始函数,并将原始函数的返回值返回进来。

after 函数同样会返回一个新函数,这个新函数会先调用原始函数,而后再调用传入的 afterFn 函数,并将原始函数的返回值返回进来。

接下来,咱们能够以一个示例函数为例,演示如何应用 beforeafter 函数:

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 函数,而后应用 beforeafter 函数,对其进行包装。咱们先应用 before 函数传入一个函数,输入一个 "before greet" 的提示信息;再应用 after 函数传入一个函数,输入一个 "after greet" 的提示信息。最初,在 newGreet 函数中调用 greet 函数,并将 'Better' 作为参数传入。

执行 newGreet('Better') 后,会依照以下程序输入三个提示信息:

before greetHello, Betterafter 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的应用场景次要有以下几点:

  1. 日志记录:在办法执行前后打印调用日志,不便排查谬误。
  2. 权限管制:对办法进行拦挡,判断用户是否有执行该办法的权限。
  3. 缓存:对于须要大量计算的办法,增加缓存性能,来缩小计算次数。

AOP的长处:

  1. 模块化:AOP能够将整个应用程序分为多个模块,不便错误处理和批改。
  2. 可复用性高:切面能够逾越多个模块的代码进行批改,使得批改变得更加容易。
  3. 易于保护:切面能够帮忙程序员更加清晰地看到代码的执行流程,升高保护老本。

AOP的毛病:

  1. 减少了代码复杂度:AOP通过减少切面来实现其性能,会减少代码的复杂度。
  2. 运行效率:AOP应用代理来实现切面,会对程序的运行效率产生肯定的负面影响。

总之,AOP在实现某些性能时能够进步代码可复用性和可维护性,但在应用时须要衡量其具体的优缺点。

本文由mdnice多平台公布