What is aspect oriented programming?

Aspect-oriented Programming (AOP) is a programming paradigm. Those of you who have done the back-end Java Web, especially those of you who have used Spring, will be familiar with it. AOP is one of the key concepts in the Spring framework. In Javascript, however, AOP is a technology that is often overlooked.

scenario

Let’s say you have an awesome calendar popover and your boss asks you to count the number of hits on a button every day. You bury a dot in the popover.

After a week, the boss said the user feedback this popup is so slow, all kinds of stuck. You want to see the average execution time of a function, so you add performance statistics code to the popover.

Over time, you will find that your business logic contains a lot of non-business stuff, even functions that you have encapsulated.

AOP, then, exists to solve such problems.

Separation of concerns

Separating business code from data statistics code (non-business code) is one of the classic uses of AOP in any language. Separating crosscutting concerns from core concerns is a core concept of AOP.

Among the common requirements on the front end are the following businesses that can be separated from the core concerns using AOP

  • Node. Js log log
  • Burying point and data reporting
  • Performance analysis, statistics function execution time
  • Add parameters to ajax requests dynamically and change function parameters dynamically
  • Separate form request and validation
  • Anti-shake and throttling

Decorator

When it comes to AOP, the decorator pattern is often confused with AOP.

Before ES6 +, to use the decorator pattern, usually by the Function. The prototype, front decoration is made before, and the Function prototype. After doing the rear decoration (see “Javascript design mode and development practices”).

Javascript introduces decorators that are syntactically similar to Java annotations, but not semantically related at all. Decorator proposals provide the ability to decorate Javascript classes and methods within those classes. (Although just function syntax sugar to run at compile time)

Report buried data

I’ll use React as an example because there are a lot of class-based components in actual development using React.

For example, there is a button in the page now, clicking the button will pop up a popup window, and at the same time, data should be reported to count how many users clicked the button to log in.

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;
Copy the code

The code above refers to the @send decorator, which modifies the prototype method on this Class. Here is the implementation of the @send decorator

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;
}
Copy the code

Before executing showDialog after the button is clicked, the desired aspect operation can be performed. We can implement AOP by wrapping the burying point, data reporting code in this decorator.

Front trim and rear trim

The send decorator above is actually a front decorator, and we can wrap it so that it can perform any function in front.

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);
        };

        returndescriptor; }}Copy the code

This allows us to use the @before decorator to cut into arbitrary non-business code before a prototype method.

function beforeLog() {
    console.log(`before calling ${name} with`.arguments);
}
class Dialog {... @before(beforeLog) showDialog(content) {// do things}... }Copy the code

Like the @before decorator, you can implement an @after decorator, except that the functions are executed in a different order.

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;
        };

        returndescriptor; }}Copy the code

Performance analysis

Sometimes we want to count the execution time of a piece of code on the user side, but we don’t want to embed the dotting code in the business code. We can also use decorators to do 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;
}
Copy the code

Just prefix the class method that counts the execution time with @measure so that the code that does the performance statistics doesn’t intrude into the business code.

class Dialog {... @measure showDialog(content) {// do things}... }Copy the code

summary

The focus of section-oriented programming is to separate the core concern from the crosscutting concern. The front end can use AOP gracefully to organize data reporting, performance analysis, statistical function execution time, dynamic change of function parameters, plug-in form validation, and so on.

reference

  • Javascript Design Patterns and Development Practices

  • AllyTeam – Improve javascript code with AOP