在Javascript中进行面向切面编程

栏目: JavaScript · 发布时间: 6年前

内容简介:面向切面编程(Aspect-oriented programming,AOP)是一种编程范式。做后端 Java web 的同学,特别是用过 Spring 的同学肯定对它非常熟悉。AOP 是 Spring 框架里面其中一个重要概念。可是在 Javascript 中,AOP 是一个经常被忽视的技术点。假设你现在有一个牛逼的日历弹窗,有一天,老板让你统计一下每天这个弹窗里面某个按钮的点击数,于是你在弹窗里做了埋点;过了一个星期,老板说用户反馈这个弹窗好慢,各种卡顿。你想看一下某个函数的平均执行时间,于是你又在弹窗

面向切面编程(Aspect-oriented programming,AOP)是一种编程范式。做后端 Java web 的同学,特别是用过 Spring 的同学肯定对它非常熟悉。AOP 是 Spring 框架里面其中一个重要概念。可是在 Javascript 中,AOP 是一个经常被忽视的技术点。

场景

假设你现在有一个牛逼的日历弹窗,有一天,老板让你统计一下每天这个弹窗里面某个按钮的点击数,于是你在弹窗里做了埋点;

过了一个星期,老板说用户反馈这个弹窗好慢,各种卡顿。你想看一下某个函数的平均执行时间,于是你又在弹窗里加上了性能统计代码。

时间久了,你会发现你的业务逻辑里包含了大量的和业务无关的东西,即使是一些你已经封装过的函数。

那么 AOP 就是为了解决这类问题而存在的。

关注点分离

分离业务代码和数据统计代码(非业务代码),无论在什么语言中,都是AOP的经典应用之一。从核心关注点中分离出横切关注点,是 AOP 的核心概念。

在前端的常见需求中,有以下一些业务可以使用 AOP 将其从核心关注点中分离出来

  • Node.js 日志log
  • 埋点、数据上报
  • 性能分析、统计函数执行时间
  • 给ajax请求动态添加参数、动态改变函数参数
  • 分离表单请求和验证
  • 防抖与节流

装饰器(Decorator)

提到 AOP 就要说到装饰器模式,AOP 经常会和装饰器模式混为一谈。

在ES6之前,要使用装饰器模式,通常通过 Function.prototype.before 做前置装饰,和 Function.prototype.after 做后置装饰(见《Javascript设计模式和开发实践》)。

Javascript 引入的 Decorator ,和 Java 的注解在语法上很类似,不过在语义上没有一丁点关系。Decorator 提案提供了对 Javascript 的类和类里的方法进行装饰的能力。(尽管只是在编译时运行的函数语法糖)

埋点数据上报

因为在使用 React 的实际开发中有大量基于 Class 的 Component,所以我这里用 React 来举例。

比如现在页面中有一个button,点击这个button会弹出一个弹窗,与此同时要进行数据上报,来统计有多少用户点击了这个登录button。

import React, { Component } from 'react';
import send from './send';

class Dialog extends Component {

    constructor(props) {
        super(props);
    }

    @send
    showDialog(content) {
        // do things
    }

    render() {
        return (
            <button onClick={() => this.showDialog('show dialog')}>showDialog</button>
        )
    }
}

export default Dialog;
复制代码

上面代码引用了 @send 装饰器,他会修改这个 Class 上的原型方法,下面是 @send 装饰器的实现

export default function send(target, name, descriptor) {
    let oldValue = descriptor.value;

    descriptor.value = function () {
        console.log(`before calling ${name} with`, arguments);
        return oldValue.apply(this, arguments);
    };

    return descriptor;
}
复制代码

在按钮点击后执行 showDialog 前,可以执行我们想要的切面操作,我们可以将埋点,数据上报相关代码封装在这个装饰器里面来实现 AOP。

前置装饰和后置装饰

上面的 send 这个装饰器其实是一个前置装饰器,我们可以将它再封装一下使它可以前置执行任意函数。

function before(beforeFn = function () { }) {
    return function (target, name, descriptor) {
        let oldValue = descriptor.value;

        descriptor.value = function () {
            beforeFn.apply(this, arguments);
            return oldValue.apply(this, arguments);
        };

        return descriptor;
    }
}
复制代码

这样我们就可以使用 @before 装饰器在一个原型方法前切入任意的非业务代码。

function beforeLog() {
    console.log(`before calling ${name} with`, arguments);
}
class Dialog {
    ...
    @before(beforeLog)
    showDialog(content) {
        // do things
    }
    ...
}
复制代码

@before 装饰器类似,可以实现一个 @after 后置装饰器,只是函数的执行顺序不一样。

function after(afterFn = function () { }) {
    return function (target, name, descriptor) {
        let oldValue = descriptor.value;

        descriptor.value = function () {
            let ret = oldValue.apply(this, arguments);
            afterFn.apply(this, arguments);
            return ret;
        };

        return descriptor;
    }
}
复制代码

性能分析

有时候我们想统计一段代码在用户侧的执行时间,但是又不想将打点代码嵌入到业务代码中,同样可以利用装饰器来做 AOP。

function measure(target, name, descriptor) {
    let oldValue = descriptor.value;

    descriptor.value = function () {
        let ret = oldValue.apply(this, arguments);
        performance.mark("startWork");
        afterFn.apply(this, arguments);
        performance.mark("endWork");
        performance.measure("work", "startWork", "endWork");
        performance
          .getEntries()
          .map(entry => JSON.stringify(entry, null, 2))
          .forEach(json => console.log(json));
        return ret;
    };

    return descriptor;
}
复制代码

在要统计执行时间的类方法前面加上 @measure 就行了,这样做性能统计的代码就不会侵入到业务代码中。

class Dialog {
    ...
    @measure
    showDialog(content) {
        // do things
    }
    ...
}
复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning JavaScript

Learning JavaScript

Shelley Powers / Oreilly & Associates Inc / 2006-10-17 / $29.99

As web browsers have become more capable and standards compliant, JavaScript has grown in prominence. JavaScript lets designers add sparkle and life to web pages, while more complex JavaScript has led......一起来看看 《Learning JavaScript》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具