感受面向切面编程

javascript/jquery

浏览数:13

2020-5-26

AD:资源代下载服务

什么是面向切面

初听面向切面编程时, 一头雾水, 什么是面向切面, 只听说过面向对象(OOP), 面向过程(PO), 函数式编程(FP), 面向切面 ? 面向的难道是某一个面?

面向搜索引擎后才了解到, 面向切面是一种编程范式(Aspect Oriented Programming), 简写 AOP, 特点是与原有逻辑解耦, 无侵入.

在后端开发工作中, 常见使用的场景是 断点调试/打印日志/…,

而在前端开发工作中, 应用场景比较灵活多变, 可以是在一次表单提交中, 在表单提交前作表单验证(前置), 或在表单提交后作数据刷新/页面跳转/Cookie 刷新等 (后置), 也可以在提交的同时作数据埋点(横向), 或是打印日志。。。
对于外部的新加入的逻辑, 为了不破坏原有的业务逻辑, 我们就可以使用 AOP 去组织代码, 分离 [业务逻辑] 与 [琐碎事务]

AOP 的关键概念点

  • 前置(before) 在目标方法执行前执行
  • 后置(after) 在目标方法执行后执行
  • 异常(after throwing)在目标方法抛出异常时执行
  • 环绕 (around) 在目标方法执行前后

前置执行函数

const before = function(fn, action) {
    return function(...args) {
        action.apply(this, args);
        const res = fn.apply(this, args);
        return { res, params: args };
    };
};

后置执行函数

const after = function(fn, action) {
    return function(...args) {
        let res = fn.apply(this, args);
        action.apply(this, args);
        return { res, params: args };
    };
};

异常执行函数

const throwing = function(fn, action) {
    let ret = { res: undefined, params: undefined };
    return function(...args) {
        try {
            const res = fn.apply(this, args);
            return (ret = { res, params: args });
        } catch (err) {
            action.apply(this, args);
            return (ret = { res: err, params: args });
        }
    };
};

环绕执行函数

const round = function(fn, actionBefore, actionAfter) {
    return function(...args) {
        actionBefore.apply(this, args);
        const res = fn.apply(this, args);
        actionAfter.apply(this, args);
        return { res, params: args };
    };
};

使用场景 — 请求记录

  const request = config => axios.request(config);
  const ButtonClickFn = function () {
    ...do something
  }
  document.querySelector('#submit').click = before(ButtonClickFn, () => {
    request({
      url: 'http://your_upload_log_url',
      method: 'GET',
      params:
        { TYPE: 'BUTTON_CLICK', POSITION: '' }
      });
  });

使用场景 — 异常处理

  const originRequest = config => axios.request(config);
  const wrapperedRequest = throwing(originRequest, function() {
    ...异常上报
  });

其它实现方式

实际上除了使用高阶函数的方法实现, 我们还可以使用 ES7 的装饰器/Ojbect.defineProperty 实现, 或基于原型链去实现

作者:跨越银河Galaxy