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日

相关文章

  • JS获取当前时间的年月日时分秒及时间的格式化的方法

    下面是针对“JS获取当前时间的年月日时分秒及时间的格式化的方法”的完整攻略。 获取时间的方式 JavaScript中可以通过以下两种方式获取当前时间: Date()对象的构造函数,例如var dateObj = new Date();,这种方式会获取当前系统时间,包括年月日时分秒等信息。 Date.now()方法,例如var timestamp = Date…

    JavaScript 2023年5月27日
    00
  • 浅析前端路由简介以及vue-router实现原理

    接下来我将为您详细讲解“浅析前端路由简介以及vue-router实现原理”的完整攻略。 前端路由简介 前端路由是指将不同的 URL 映射至不同的视图,并且不跳转页面的技术。它的出现解决了传统网页的后退问题,提升了交互体验。前端路由通常使用 HTML5 的 history API 实现。 在前端路由中,通常需要以下几个组成部分: 路由表:定义了 URL 与视图…

    JavaScript 2023年6月11日
    00
  • 微信小程序结合mock.js实现后台模拟及调试

    下面是“微信小程序结合mock.js实现后台模拟及调试”的完整攻略,包含以下几个步骤: 1.安装mock.js 在小程序目录下,使用npm安装mock.js: npm install mockjs –save-dev 2.创建mock数据文件 在小程序目录下,创建一个mock文件夹,然后在mock文件夹下创建一个mock.js文件,在该文件中编写mock数…

    JavaScript 2023年6月11日
    00
  • ascii码表(二进制 十进制 十六进制)详细介绍

    ASCII码表(二进制、十进制、十六进制)详细介绍 什么是ASCII码表? ASCII码表(American Standard Code for Information Interchange)是一种用于将字符编码为数字的字符编码标准。它最初是在美国为电传打字机而设计的,但现在已经成为计算机系统和通信设备中使用的标准字符集。 ASCII码表的编码方式 ASC…

    JavaScript 2023年5月19日
    00
  • 用javascript添加控件自定义属性解析

    下面是一份关于用JavaScript添加控件自定义属性解析的攻略: 自定义属性 在HTML标签中,我们可以添加自己的属性,这些属性也被称为自定义属性。这些自定义属性一般不会被浏览器识别和解析,但是在JavaScript中,我们可以通过getAttribute和setAttribute方法来获取和设置这些自定义属性的值,从而实现一些我们想要的效果。 添加自定义…

    JavaScript 2023年6月10日
    00
  • js实现滑动轮播效果

    当我们需要在网站中展示多个幻灯片图片时,掌握JavaScript实现滑动轮播效果非常重要。以下是实现此效果的完整攻略: 步骤一: HTML结构 在HTML中创建一个轮播区域,它包含一个有序列表,以及向前和向后按钮。 <div class="slider"> <ul class="slider-wrapper&q…

    JavaScript 2023年6月11日
    00
  • JS实现电话号码的字母组合算法示例

    JS实现电话号码的字母组合算法示例可以用来解决以下问题:给定一个数字字符串,返回该数字字符串代表的电话号码的所有字母组合。 算法思路 该算法可以使用递归的方式进行实现。在递归过程中,所有可能的组合都存储在一个数组中,初始值为[“”]。在每次递归过程中,取出数组中的第一个元素,根据当前数字所代表的字母,依次添加到该元素的末尾,生成新的字符串插入到数组中。具体实…

    JavaScript 2023年5月28日
    00
  • 详解js的事件处理函数和动态创建html标记方法

    下面是详解”js的事件处理函数和动态创建HTML标记方法”的完整攻略。 1. 事件处理函数 1.1 什么是事件处理函数? 事件处理函数是在特定事件发生时被调用的函数。在JavaScript中,我们可以使用事件处理函数来处理各种事件,比如单击、鼠标悬停、按键等等事件。 1.2 如何使用事件处理函数? 我们可以使用addEventListener()方法将事件处…

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