• aop


     aop  - 切面编程,主要用于执行一些与业务不相关的操作。

    比如上报数据、日志输出等与业务不强相关的操作,把这些非业务操作与业务流程解耦。

    还有其它使用情况,比如有一个通用弹层,打开弹层和关闭弹层都有一些操作,比如打开弹层需要把body的颜色变成灰色,然后关闭弹层需要把颜色还原等这样的操作。正常的解决方法是

    需要增加beforeOpen,beforeClose这样的回调函数,需要接口提供对应的参数接口,如果需要在打开弹层之后还需要提供 afterOpen等这样的方法。但是如果弹层支持aop就可以直接对open方法进行修改了。

    一般情况下,aop是用在同步函数中使用的,但是有时候异步函数也需要用到,比如:请求列表的数据,需要把数据列表的盒展示成loading状态,数据请求回来之后,再去掉loading状态。所以我写了个工具函数,也支持

    返回Promise的函数、async函数、Generator函数

    aop通用方法完整案例如下:

    export function isFunction(arg) {
      return typeof arg === "function";
    }
    export function isGeneratorFunction(obj) {
      return obj && obj.constructor && "GeneratorFunction" === obj.constructor.name;
    }
    export function isAsyncFunction(obj) {
      return obj && obj.constructor && "AsyncFunction" === obj.constructor.name;
    }
    export function isPromise(obj) {
      return obj && "function" === typeof obj.then;
    }
    /**
     * 切面编程
     * @param {Function | GeneratorFunction | AsyncFunction} fn - 方法
     * @param {*} beforeFn - 前置方法
     * @param {*} afterFn - 后置方法
     */
    export function aop(fn, beforeFn, afterFn) {
      if (isGeneratorFunction(fn)) {
        const newFn = function(...args) {
          const me = this;
          if (isFunction(beforeFn)) {
            beforeFn.apply(me, args);
          }
          let ret = fn.apply(me, args);
          let next = ret.next;
          ret.next = function(...arg) {
            const done = next.apply(ret, arg);
            if (done.done) {
              if (isFunction(afterFn)) {
                afterFn.apply(me, args);
              }
            }
            return done;
          };
          return ret;
        };
        for (let key in fn) {
          if (fn.hasOwnProperty(key)) {
            newFn[key] = fn[key];
          }
        }
        return newFn;
      }
    
      if (isAsyncFunction(fn)) {
        const newFn = async function(...args) {
          const me = this;
          if (isFunction(beforeFn)) {
            beforeFn.apply(me, args);
          }
          return await fn
            .apply(me, args)
            .then((...data) => {
              if (isFunction(afterFn)) {
                afterFn.apply(me, data);
              }
              return data;
            })
            .catch((...err) => {
              if (isFunction(afterFn)) {
                afterFn.apply(me, err);
              }
              throw err;
            });
        };
        for (let key in fn) {
          if (fn.hasOwnProperty(key)) {
            newFn[key] = fn[key];
          }
        }
        return newFn;
      }
    
      if (isFunction(fn)) {
        const newFn = function(...args) {
          const me = this;
          if (isFunction(beforeFn)) {
            beforeFn.apply(me, args);
          }
          const ret = fn.apply(me, args);
          if (isFunction(afterFn)) {
            if (isPromise(ret)) {
              ret
                .then((...data) => {
                  afterFn.apply(me, data);
                })
                .catch((...err) => {
                  afterFn.apply(me, err);
                });
            } else {
              afterFn.apply(me, args);
            }
          }
          return ret;
        };
        for (let key in fn) {
          if (fn.hasOwnProperty(key)) {
            newFn[key] = fn[key];
          }
        }
        return newFn;
      }
    }
    export function before(fn, callback) {
      return aop(fn, callback, null);
    }
    export function after(fn, callback) {
      return aop(fn, null, callback);
    }
    export function wrap(fn, beforeFn, afterFn) {
      return aop(fn, beforeFn, afterFn);
    }
    export function aopFactory(fn) {
      //非函数或者已有aop方法原样输出
      if (typeof fn !== "function" || fn.before || fn.after || fn.wrap) {
        return fn;
      }
      fn.before = function(callback) {
        const me = this;
        return before(me, callback);
      };
      fn.after = function(callback) {
        const me = this;
        return after(me, callback);
      };
      fn.wrap = function(beforeFn, afterFn) {
        const me = this;
        return wrap(me, beforeFn, afterFn);
      };
      return fn;
    }

    使用案例 - 普通方法调用:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>普通函数</title>
    </head>
    
    <body>
        <script src="./aop.js"></script>
        <script>
            function pureFn() {
                console.log('我是一个普通的函数');
            }
            const aopFn = aopFactory(pureFn);
            const fn = aopFn.before(function () {
                console.log('我是前置方法')
            })
                .after(function () {
                    console.log('我是后置方法')
                });
            fn();
        </script>
    
    </body>
    
    </html>

     使用案例 - async方法调用 :  

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>普通函数</title>
    </head>
    
    <body>
        <script src="./aop.js"></script>
        <script>
            async function asyncFn() {
                const ret = await new Promise((resolve) => {
                    setTimeout(() => {
                        console.log('异步函数内部操作')
                        resolve('我是async方法,返回值。')
                    }, 1000);
                })
                console.log(`ret: ${ret}`)
                return ret;
            }
            const aopFn = aopFactory(asyncFn);
            const fn = aopFn.before(function () {
                console.log('我是前置方法')
            })
                .after(function () {
                    console.log('我是后置方法')
                });
            fn()
                .then((data) => {
                    console.log(data);
                })
                .finally(() => {
                    console.log('finally');
                })
        </script>
    
    </body>
    
    </html>

    结果:

    使用案例 -  promise方法 :   

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>普通函数</title>
    </head>
    
    <body>
        <script src="./aop.js"></script>
        <script>
            function promiseFn() {
                return new Promise((resolve, reject) => {
                    setTimeout(() => {
                        resolve('返回内容')
                    }, 1000)
    
                })
            }
            const aopFn = aopFactory(promiseFn);
            const fn = aopFn.before(function () {
                console.log('前置方法')
            })
                .after(function () {
                    console.log('后置方法')
                });
            fn().then((data) => {
                console.log(data);
            });    
        </script>
    
    </body>
    
    </html>

    注意这里有个小问题是,这个after的方法是在异步之后执行的,但是是在其它then之前执行的。

  • 相关阅读:
    一个简单的开源PHP爬虫框架『Phpfetcher』
    OpenNI1.5获取华硕XtionProLive深度图和彩色图并用OpenCV显示
    【数字图像处理之(三)】用图像增强谈灰度变换
    Android 实现登录界面和功能实例
    MFC通过button控制编辑框是否显示系统时间
    springmvc+mybatis整合
    iOS获取当前城市
    Android之Wifi学习(1)
    实测:Windows 8.1 (Windows Blue) 第三方桌面应用无法支持Retina屏,效果与Windows8.0似无差别。
    java 读取不同的配置文件
  • 原文地址:https://www.cnblogs.com/hellolol/p/11659921.html
Copyright © 2020-2023  润新知