JavaScript实现AOP详解(面向切面编程,装饰者模式)

JavaScript实现AOP详解

什么是AOP

AOP(Aspect Oriented Programming),中文译为面向切面编程,是一种编程方法论。它通过预编译方式和运行期动态代理实现程序功能的统一维护的方法。

AOP 解决的问题主要是将那些与业务无关,却为业务模块所共同调用的逻辑或责任进行封装,并分离出来,例如在不修改源代码的情况下统一添加日志记录和权限控制等。

AOP的应用场景

  • 日志记录
  • 接口请求校验(鉴权)
  • 性能统计
  • 事务处理

实现方式

AOP的实现方式主要有下面两种:

  • 静态AOP
  • 动态AOP

在JavaScript中,由于没有编译过程,我们只能使用动态AOP来实现。

动态AOP

动态AOP可以通过vscode插件node-debug2来调试,并实现对方法调用的控制。它的实现方式是将一个函数在运行时动态地加入到对象中。
这个函数可以在原函数调用前、调用后,抑或调用前后都被执行,从而实现AOP。
我们将这些增强一个函数的方法称之为"切面"

看下面的代码:

Function.prototype.before = function(beforeFn){
    var self = this; // 保存原函数的引用
    return function() { // 返回包含了原函数和新函数的"代理"函数
        beforeFn.apply(this, arguments); // 执行新函数,且保证this不被劫持,新函数接受的参数
        // 也会被原封不动地传入原函数,新函数在原函数之前执行
          return self.apply(this, arguments); // 执行原函数并返回原函数的执行结果,
        // 并且保证this不被劫持
    }
}
Function.prototype.after=function(afterFn) {
    var self = this;
    return function(){
        var ret = self.apply(this,arguments);
        afterFn.apply(this,arguments);
        return ret;
    }
}

我们在Function.prototype上新增两个方法,before和after。

  • before方法:在原函数执行之前执行一个函数。
  • after方法:在原函数执行之后执行一个函数。

现在我们定义了一个方法,用于向用户显示订单详情:

function showOrder(order) {
  console.log(`订单号: ${order.no}\n名称:${order.name}\n价格:${order.price}\n数量:${order.quantity}\n总价:${order.price * order.quantity}\n`);
}
const order = {
  no: '123',
  name: '衣服',
  price: 100,
  quantity: 2
};
showOrder(order);

现在我们想在这个方法执行前输出一行日志,如下:

function log(){
console.log(`[${new Date()}] 启动方法: ${showOrder.name}`);
}
showOrder.before(log);

这样做就可以在showOrder方法执行前输出一行日志了。

我们同样可以在方法执行后输出返回结果的日志:

function log2(ret){
console.log(`[${new Date()}] ${showOrder.name}返回: ${ret}`);
}
showOrder.after(log2);

这样做就可以在showOrder方法执行后输出结果了。

完整代码如下:

Function.prototype.before = function(beforeFn){
    var self = this; // 保存原函数的引用
    return function() { // 返回包含了原函数和新函数的"代理"函数
        beforeFn.apply(this, arguments); // 执行新函数,且保证this不被劫持,新函数接受的参数
        // 也会被原封不动地传入原函数,新函数在原函数之前执行
          return self.apply(this, arguments); // 执行原函数并返回原函数的执行结果,
        // 并且保证this不被劫持
    }
}
Function.prototype.after=function(afterFn) {
    var self = this;
    return function(){
        var ret = self.apply(this,arguments);
        afterFn.apply(this,arguments);
        return ret;
    }
}

function showOrder(order) {
  console.log(`订单号: ${order.no}\n名称:${order.name}\n价格:${order.price}\n数量:${order.quantity}\n总价:${order.price * order.quantity}\n`);
}
const order = {
  no: '123',
  name: '衣服',
  price: 100,
  quantity: 2
};

function log(){
  console.log(`[${new Date()}] 启动方法: ${showOrder.name}`);
}
function log2(ret){
  console.log(`[${new Date()}] ${showOrder.name}返回: ${ret}`);
}

showOrder.before(log);
showOrder.after(log2);

showOrder(order);

AOP装饰器模式

装饰器模式是指在不改变对象自身的基础上,在程序运行期间来动态修改其功能的方式。
通常,装饰器模式把一个对象嵌套在另一个对象中,以实现对原对象的功能增强。

看下面的代码:

Function.prototype.before = function(beforeFn){
    var self = this; // 保存原函数的引用
    return function() { // 返回包含了原函数和新函数的"代理"函数
        beforeFn.apply(this, arguments); // 执行新函数,且保证this不被劫持,新函数接受的参数
        // 也会被原封不动地传入原函数,新函数在原函数之前执行
          return self.apply(this, arguments); // 执行原函数并返回原函数的执行结果,
        // 并且保证this不被劫持
    }
}
Function.prototype.after=function(afterFn) {
    var self = this;
    return function(){
        var ret = self.apply(this,arguments);
        afterFn.apply(this,arguments);
        return ret;
    }
}

class ShowOrder {
  show(order) {
    console.log(`订单号: ${order.no}\n名称:${order.name}\n价格:${order.price}\n数量:${order.quantity}\n总价:${order.price * order.quantity}\n`);
  }
}

const order = {
  no: '123',
  name: '衣服',
  price: 100,
  quantity: 2
};
const showOrder = new ShowOrder().show;

function log(){
console.log(`[${new Date()}] 启动方法: ${showOrder.name}`);
}
showOrder = showOrder.before(log);

function log2(ret){
console.log(`[${new Date()}] ${showOrder.name}返回: ${ret}`);
}
showOrder = showOrder.after(log2);

showOrder(order);

通过装饰器模式,我们实现了对某一个对象方法的AOP增强。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:JavaScript实现AOP详解(面向切面编程,装饰者模式) - Python技术站

(0)
上一篇 2023年5月27日
下一篇 2023年5月27日

相关文章

  • Javascript Math abs() 方法

    JavaScript中的Math.abs()方法用于返回一个数的绝对值。该方法接受一个参数,即要计算绝对值的数值。以下是关于Math.abs()方法的完整攻略,包括两个示例。 JavaScript Math对象中的abs()方法 JavaScript Math对象中的abs()方法用于返回一个数的绝对值。该方法接受一个参数,即要计算绝对值的数值。 下面是ab…

    JavaScript 2023年5月11日
    00
  • python中altair可视化库实例用法

    下面是“python中altair可视化库实例用法”的完整攻略: 1. Altair 库简介 Altair 是一个基于 Python 的声明式可视化库,用于创建交互式可视化图表。 声明式语法是指你通过直接描述所需图表的方式来创建它们,而无需编写细节代码。 Altair 是对 Vega-Lite 的 Python 封装,Vega-Lite 是基于 Vega 开…

    JavaScript 2023年5月28日
    00
  • js实现盒子滚动动画效果

    下面是关于”js实现盒子滚动动画效果”的完整攻略: 1.编写HTML结构 首先,在HTML文件中编写盒子结构,例如: <div class="container"> <div class="box" style="background-color: red;">Box 1&l…

    JavaScript 2023年6月10日
    00
  • 关于JavaScript中var声明变量作用域的推断

    首先,需要了解一些JavaScript中变量作用域的相关知识。 在JavaScript中,变量的作用域有全局作用域和局部作用域两种。 在全局作用域中声明的变量可以被程序中任意位置访问,而在局部作用域中声明的变量仅可以在声明该变量的函数内及函数内部任意嵌套的其他函数内访问。 在ES5之前,JavaScript中只有函数作用域,因此无法使用块级作用域。即使在if…

    JavaScript 2023年6月10日
    00
  • JS字符串长度判断,超出进行自动截取的实例(支持中文)

    实现JS字符串长度判断并进行自动截取支持中文的方法主要涉及以下步骤: 获取字符串长度JavaScript中获取字符串长度的方法是通过字符串的length属性获取,例如 var str = ‘Hello World’; console.log(str.length); // 11 判断是否超出限制根据所需的字符串长度限制和当前字符串长度,可以使用if语句或三元…

    JavaScript 2023年5月28日
    00
  • 如何为你的JavaScript代码日志着色详解

    下面是关于如何为JavaScript代码日志着色的完整攻略: 为什么需要为JavaScript代码日志着色 当我们在开发JavaScript应用程序时,经常需要查看日志信息来调试代码、排除错误等。但是,当日志信息量过大时,我们很难一眼区分出哪些是错误信息、哪些是调试信息、哪些是警告信息等。因此,着色的日志信息能够更快更直观地帮助我们了解代码的执行情况,提高代…

    JavaScript 2023年5月28日
    00
  • XMLHttpRequest对象_Ajax异步请求重点(推荐)

    XMLHttpRequest对象_Ajax异步请求重点(推荐) 什么是Ajax异步请求 Ajax全称为Asynchronous Javascript And XML,翻译成中文是“异步的 JavaScript 和 XML”。Ajax技术是一种在不重新加载整个页面的情况下,通过后台与服务器进行少量数据交换,实现页面的局部刷新,从而提高页面的响应速度和用户体验。…

    JavaScript 2023年6月11日
    00
  • Javascript Date setUTCMinutes() 方法

    以下是关于JavaScript Date对象的setUTCMinutes()方法的完整攻略,包括两个示例说明。 JavaScript Date对象的setUTCMinutes()方法 JavaScript的setUTCMinutes()方法设置对象UTC分钟部分。该方法接受一个整数,表示要设置的UTC分钟。如果该参数超出了JavaScript所能表示的范围,…

    JavaScript 2023年5月11日
    00
合作推广
合作推广
分享本页
返回顶部