Gahing's blog Gahing's blog
首页
知识体系
  • 前端基础
  • 应用框架
  • 工程能力
  • 应用基础
  • 专业领域
  • 业务场景
  • 前端晋升 (opens new window)
  • Git
  • 网络基础
  • 算法
  • 数据结构
  • 编程范式
  • 编解码
  • Linux
  • AIGC
  • 其他领域

    • 客户端
    • 服务端
    • 产品设计
软素质
  • 面试经验
  • 人生总结
  • 个人简历
  • 知识卡片
  • 灵感记录
  • 实用技巧
  • 知识科普
  • 友情链接
  • 美食推荐 (opens new window)
  • 收藏夹

    • 优质前端信息源 (opens new window)
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Gahing / francecil

To be best
首页
知识体系
  • 前端基础
  • 应用框架
  • 工程能力
  • 应用基础
  • 专业领域
  • 业务场景
  • 前端晋升 (opens new window)
  • Git
  • 网络基础
  • 算法
  • 数据结构
  • 编程范式
  • 编解码
  • Linux
  • AIGC
  • 其他领域

    • 客户端
    • 服务端
    • 产品设计
软素质
  • 面试经验
  • 人生总结
  • 个人简历
  • 知识卡片
  • 灵感记录
  • 实用技巧
  • 知识科普
  • 友情链接
  • 美食推荐 (opens new window)
  • 收藏夹

    • 优质前端信息源 (opens new window)
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 前端基础

    • 编程语言

      • CSS

      • HTML

      • JavaScript

      • Rust

      • TypeScript

        • TypeScript学习笔记
        • typescript与aop
          • 前言
          • aop 介绍
          • 实现
            • (1)返回一个新的原型方法
            • (2)内部保留上一次原型方法
            • (3)组合了前两者的优点,内部保留上一次原型方法,支持同时配置多个增强
          • 拓展阅读
      • WebAssembly

    • 开发工具

    • 前端调试

    • 浏览器原理

    • 浏览器生态

  • 应用框架

  • 工程能力

  • 应用基础

  • 专业领域

  • 业务场景

  • 大前端
  • 前端基础
  • 编程语言
  • TypeScript
gahing
2021-05-08
目录

typescript与aop草稿

# 前言

需要对 puppeteer api 的执行进行日志打点,尽量不要影响到原始方法执行逻辑

# aop 介绍

面向切面编程

切点、切面、增强

# 实现

3 种方式

# (1)返回一个新的原型方法

xxx.prototype.xxx = aop(xxx.prototype.xxx, {
    before: ()=>{},
    after: ()=>{}
})
1
2
3
4

多个切入点使用一个原型方法,如果有多次 before ,应该在一个 before 方法里处理

// 错误处理
xxx.prototype.xxx = aop(xxx.prototype.xxx, {
    before: ()=>{
    }
})
// 上面的处理会丢失
xxx.prototype.xxx = aop(xxx.prototype.xxx, {
    before: ()=>{
    }
})
// 正确
xxx.prototype.xxx = aop(xxx.prototype.xxx, {
    before: ()=>{
        before1();
        before2();
    }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

缺点:不支持多次配置同类增强

# (2)内部保留上一次原型方法

另一种是无返回值,每次增强后都是一个新的原型对象,内部会保存上一次修改的原型方法。

原型方法调用后,是一个递归调用的过程

aop.before(xxx,'xxxx', function(){

})
aop.after(xxx,'xxxx', function(){
    
})
1
2
3
4
5
6

缺点:配置不同类增强时,导致内部调用是一个递归回调的过程

# (3)组合了前两者的优点,内部保留上一次原型方法,支持同时配置多个增强

aop(xxx, 'xxx', {
    before: ()=>{},
    after:()=>{}
})
aop(xxx, 'xxx', {
    before: ()=>{},
})
1
2
3
4
5
6
7
interface JoinPoint<T> {
    target: T;
    method: Function;
    args: any;
    self: any;
}
interface Options<T> {
    before?: (joinPoint: JoinPoint<T>) => void;
    after?: (joinPoint: JoinPoint<T>, result?: any, error?: Error) => void;
    afterReturn?: (joinPoint: JoinPoint<T>, result: any) => any;
    afterThrow?: (joinPoint: JoinPoint<T>, error: Error) => boolean;
    around?: (joinPoint: JoinPoint<T>, handle: (...args: any) => any) => void;
}


let findPointCut = (target: any, pointCut: string) => {
    if (typeof pointCut === 'string') {
        let func = target.prototype[pointCut];
        // 暂不支持属性的 aop
        if (typeof func === 'function') {
            return func;
        }
    }
    return null;
};

const advice = <T>(target: T, pointCut: string, advice: Options<T>) => {
    let oldPrototypeFunc = findPointCut(target, pointCut);
    if (!oldPrototypeFunc) {
        return;
    }
    (target as any).prototype[pointCut] = function(...args: any) {
        let self = this;
        let joinPoint: JoinPoint<T> = {
            target,
            method: oldPrototypeFunc,
            args,
            self
        };
        let { before, around, after, afterReturn, afterThrow } = advice;
        // 前置增强
        before && before.call(self, joinPoint);

        // 实际执行的函数
        let actual: any = () => {
            return oldPrototypeFunc.apply(self, args);
        };

        // 环绕增强
        let roundHandle;
        if (around) {
            // 使用增强方法替换原型方法
            actual = around;
            roundHandle = (...newArgs: any) => {
                // 若 handle 未传入参数,则采用原来参数
                return oldPrototypeFunc.apply(self, newArgs.length > 0 ? newArgs : args);
            };
        }

        const hasAfterAdvice = after || afterReturn || afterThrow;
        if (!hasAfterAdvice) {
            // 未定义任何后置增强,直接执行原方法
            return actual.call(self, joinPoint, roundHandle);
        }

        let result;
        let error: Error | undefined;
        try {
            result = actual.call(self, joinPoint, roundHandle);
            // 返回增强
            return (afterReturn && afterReturn.call(self, joinPoint, result)) || result;
        } catch (e) {
            error = e;
            // 异常增强,根据返回值看是否拦截异常
            let shouldIntercept = afterThrow && afterThrow.call(self, joinPoint, e);
            if (!shouldIntercept) {
                throw e;
            }
        } finally {
            // 后置增强,不影响返回结果
            after && after.call(self, joinPoint, result, error);
        }
    };
};

function aop<T>(target: T, pointCut: string, options: Options<T>) {
    advice(target, pointCut, options);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

测试demo1

import { Page } from 'puppeteer';
const { Page } = require('puppeteer/lib/cjs/puppeteer/common/Page');
aop<Page>(Page, 'click', {
    before: ({ target }) => {},
    around:({},handle)=>{

    }
});
1
2
3
4
5
6
7
8

测试demo2

class A {
    say(){}
    eat(){}  
    async asyncFunc(){
        return new Promise((resolve)=>{
            console.log('start asyncFunc')
            setTimeout(()=>{
                console.log('end asyncFunc')
                resolve('hh')
            }, 3000)
        })
    }
}
let a = new A()
aop(A, 'say', {
    before:()=>console.log('b1'),
    after:()=>console.log('a1')
})
aop(A, 'asyncFunc', {
    before:()=>console.log('b2'),
    after:(point, result)=>console.log('a2', result)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 拓展阅读

https://juejin.cn/post/6844903958301900807#heading-3

https://juejin.cn/post/6844903858649432078#heading-13

编辑 (opens new window)
上次更新: 2024/09/01, 23:56:56
TypeScript学习笔记
WebAssembly 初探

← TypeScript学习笔记 WebAssembly 初探→

最近更新
01
浅谈代码质量与量化指标
08-27
02
快速理解 JS 装饰器
08-26
03
Vue 项目中的 data-v-xxx 是怎么生成的
09-19
更多文章>
Theme by Vdoing | Copyright © 2016-2024 Gahing | 闽ICP备19024221号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式