ECMAScript Decorators– Decorators

What is the Decorators

  • Decorators can change the properties and behavior of class methods and class instance fields, giving us the flexibility to implement them dynamically using simpler syntax, noninvasive. — For example, if you add a cover to your phone, it doesn’t affect the phone’s original functions such as calling and charging

Application scenarios

  • A classic use of Decorators is AOP programming, such as “logging systems,” which record the behavior of a system by adding logging sessions without affecting the functionality of the original system
  • More abstractly, you can think of it as a layer of filters for data streams, so typical uses of AOP include security checking, caching, debugging, persistence, and so on.

The principle of

  • The nature of Decorators is to take advantage of ES5’s Object.defineProperty property. These three parameters are identical to the Object.defineProperty parameter and therefore cannot be changed
  • The object must be. This could be a native JavaScript object (that is, a user-defined object or a built-in object) or a DOM object.
  • Propertyname required. A string containing the name of the property
  • Descriptor required. Property descriptor. It can be for data attributes or accessor attributes.
  • For example
var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};
// modify property descriptor
Object.defineProperty( myObj, 'myPropOne', {
    writable: false// Allow this property to change enumerable:false// Allow keys to be enumerated, in other wordsfor inKeys () does not output any key 64x:false// Whether the target attribute can be deleted or whether the attribute can be modified again});Copy the code

Application, for example,

  1. Class methods @ readonly
class User {
    constructor( firstname, lastName ) {
        this.firstname = firstname;
        this.lastName = lastName;
    }
    
    @readonly
    getFullName() {
        return this.firstname + ' ' + this.lastName;
    }
}
// create instance
let user = new User( 'John'.'Doe'); console.log( user.getFullName() ); / / one day I accidentally override this method to the User. The prototype. GetFullName =function() {
    return 'HACKED! '; } // Print HACKED! // Method 1 is this the best solution? Object.defineproperty (user.prototype,'getFullName', {
    writable: false}); // Add this method to the decorated method getFullNamefunction readonly( target, property, descriptor ) {
    descriptor.writable = false;
    return descriptor;
}
Copy the code
  1. Class method @log Logs are printed
function log( logMessage ) {
    // return decorator function
    return function ( target, property, descriptor ) {
        // save original value, which is method (function)
        let originalMethod = descriptor.value;
        // replace method implementation
        descriptor.value = function( ...args ) {
            console.log( '[LOG]'.logMessage );
            // here, call original method
            // `this` points to the instance
            returnoriginalMethod.call( this, ... args ); };return descriptor;
    }
}
class User {
    constructor( firstname, lastName ) {
        this.firstname = firstname;
        this.lastName = lastName;
    }
    @log('calling getFullName method on User class')
    getFullName() {
        return this.firstname + ' ' + this.lastName;
    }
}
var user = new User( 'John'.'Doe' );
console.log( user.getFullName() );
Copy the code
  1. Class attribute case conversion
Initializer function is used internally by Babel to create the value of the property descriptor for object propertiesfunction toCase( CASE = 'lower' ) {
    return function ( target, name, descriptor ) {
        let initValue = descriptor.initializer();
    
        descriptor.initializer = function() {return ( CASE == 'lower')? initValue.toLowerCase() : initValue.toUpperCase(); }return descriptor;
    }
}
class User {
    @toCase( 'upper' )
    firstName = 'default_first_name';
    lastName = 'default_last_name';
    constructor( firstName, lastName ) {
        if( firstName ) this.firstName = firstName;
        if( lastName ) this.lastName = lastName;
    }
    getFullName() {
        return this.firstName + ' ' + this.lastName;
    }
}
console.log( new User() );
Copy the code
  1. Class decorator
function withLoginStatus( UserRef ) {
  returnclass extends UserRef { constructor( ... args ) { super( ... args ); this.isLoggedIn =false;
      }
      setLoggedIn() {
          this.isLoggedIn = true; } } } @withLoginStatus class User { constructor( firstName, lastName ) { this.firstName = firstName; this.lastName = lastName; }}let user = new User( 'John'.'Doe' );
console.log( 'Before ===> ', user );
// set logged in
user.setLoggedIn();
console.log( 'After ===> ', user );
Copy the code

Babel is installed in

  • Online conversion link (if you cannot open the web page, you need to turn over the wall)
  • Build the Babel install
3. NPM install babel-core babel-plugin-transform-decorators 4 Babel-plugin-transform-decorators-legacy --save-dev."plugins": [    
    "transform-decorators-legacy"]}Copy the code

Refer to the link

  • ECMAScript Decorators– Decorators
  • ES7 Decorator Decorator pattern
  • Ruan Yifeng — decorator