Many object-oriented languages have Decorator functions that modify the behavior of a class. Currently, this approach has been introduced in ES7, but neither mainstream browsers nor Node.js are particularly compatible with it.

Therefore, to use decorators in a project, either use Babel for translation or use Typescript, a superset of Javascript for development.

If the details of the grammar is not very understanding, can the advanced portal: http://es6.ruanyifeng.com/#docs/decorator, along with nguyen other teachers learn about its features.

The original

The idea behind using decorators is to make one thing better without having to modify the original interface. Just like:

  • The mobile phone can be used, but the case can be anti-drop;
  • Chairs can be sat on, but cushions make it more comfortable;
  • A rifle can shoot, but with a scope it can shoot more accurately.
  • .

More abstractly, in the computer world, it can be applied to log collection, error capture, security checking, caching, debugging, persistence, and so on.

A common decorator

Common decorators include class decorators, method decorators, and, of course, property decorators, but we won’t discuss them more often.

Class decorator

Applies primarily to class constructors that take the constructor of the class:

function testable(target) {
    target.prototype.isTestable = true
}

@testable
class MyTestableClass {}

let obj = new MyTestableClass()
obj.isTestable // true
Copy the code

If you add a method to target, you get a static method. If you add a method to target, you get a static method. If you want to add instance attributes, you can do so through the Prototype object of the target class.

Now we implement a decorator that captures the execution time of a method using a class decorator:

const sleepTimeClass = (timeHandler? : (time? : number) = > void) = >(target: any) = > {
    Object.getOwnPropertyNames(target.prototype).forEach(key= > {
        const func = target.prototype[key]
        target.prototype[key] = async(... args: any[]) => {const startTime = await +new Date(a)await func.apply(this, args)
            const endTime = await +new Date()
            timeHandler && await timeHandler(endTime - startTime)
        }
    })
    return target
}
Copy the code

The reason for enclosing a layer of functions is to allow the user to further process the execution time through Currization:

const sleepTimeClassTimer = sleepTimeClass(time= > {
    console.log('Execution time'.`${time}ms`)
})

@sleepTimeClassTimer
class homepageController {
    async get(ctx: any) {
        ctx.response.body = await pageService.homeHtml('/page/helloworld'.'/page/404')}}Copy the code

This will print out the corresponding execution time each time a method in the class completes execution.

Method decorator

function readonly(target, name, descriptor){
    The original value of the // Descriptor object is as follows
    / / {
    // value: specifiedFunction,
    // enumerable: false,
    // configurable: true,
    // writable: true
    // }
    descriptor.writable = false
    return descriptor
}

readonly(Person.prototype, 'name', descriptor)
/ / similar to
Object.defineProperty(Person.prototype, 'name', descriptor)
Copy the code

Since async and await exceptions are difficult to catch in asynchronous programming, if you force a try… The code still looks ugly, so using decorators is easy:

const asyncMethod = (errorHandler? : (error? :Error) = > void) = >(. args: any[]) = > {
    const func = args[2].value
    return {
        get() {
            return (. args: any[]) = > {
                return Promise.resolve(func.apply(this, args)).catch(error= > {
                    errorHandler && errorHandler(error)
                })
            }
        },
        set(newValue: any) {
            return newValue
        }
    }
}
Copy the code

Next use the method decorator:

const errorAsyncMethod = asyncMethod(error= > {
    console.error('Error warning', error)
})

class homepageController {
    @errorAsyncMethod async get(ctx: any) {
        ctx.response.body = await pageService.homeHtml('/page/helloworld'.'/page/404')}}Copy the code

Decorator load order

A class or method can nest many decorators, so it is important to know the order in which they are executed:

  • When there are multiple parameter decorators, proceed from the last parameter.
  • The parameter decorator in method and method parameters executes first;
  • Class decorators are always executed last;
  • Method and property decorators, which execute first;
  • Because parameters are part of the method, they are always executed right next to the method.

The application of decorators

As originally mentioned, wouldn’t it be fun to imagine basic performance and log monitoring on the front and back ends with just a few decorators?