乐趣区

关于程序员:JS中的面向切片编程AOP

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

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

AOP 的长处:

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

AOP 的毛病:

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

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

本文由 mdnice 多平台公布

退出移动版