ECMAScript Decorators---裝飾器

chen小白發表於2018-06-28

ECMAScript Decorators---裝飾器

Decorators是什麼

  • Decorators可以改變類方法和類例項欄位的屬性和行為,使我們可以靈活地使用更簡單的語法動態實現這些內容,是非侵入式的。---舉例,你給手機新增一個外殼罷了,並不影響手機原有的通話、充電等功能

應用場景

  • Decorators的經典的應用是AOP程式設計,比如“日誌系統”,日誌系統的作用是記錄系統的行為操作,它在不影響原有系統的功能的基礎上增加記錄環節
  • 更加抽象的理解,可以理解為給資料流做一層filter,因此 AOP 的典型應用包括安全檢查、快取、除錯、持久化等等。

原理

  • Decorators的本質是利用了ES5的Object.defineProperty屬性,這三個引數其實是和Object.defineProperty引數一致的,因此不能更改
  • object 必需。要在其上新增或修改屬性的物件 這可能是一個本機JavaScript物件(即使用者定義的物件或內建物件)或 DOM 物件。
  • propertyname必需。一個包含屬性名稱的字串
  • descriptor 必需。 屬性描述符。它可以針對資料屬性或訪問器屬性。
  • 舉例說明
var myObj = {
    myPropOne: 1,
    myPropTwo: 2
};
// modify property descriptor
Object.defineProperty( myObj, 'myPropOne', {
    writable: false, // 是否允許該屬性值更改
    enumerable: false, // 是否允許key被列舉,話句話說for in 或者Object.keys() 不會輸出key
    configurable: false// 目標屬性是否可以被刪除或是否可以再次修改特性
} );

複製程式碼

應用舉例

  1. 類方法 @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() );

// 某天我不小心重寫了這個方法
User.prototype.getFullName = function() {
    return 'HACKED!';
}

// 輸出 HACKED! 與預期不符,怎麼避免此類情況發生

// 方法1 這是最好的解決方案麼?修飾器登場
Object.defineProperty( User.prototype, 'getFullName', {
    writable: false
});

// 將此方法新增到修飾方法getFullName上
function readonly( target, property, descriptor ) {
    descriptor.writable = false;
    return descriptor;
}
複製程式碼
  1. 類方法 @log日誌列印
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
            return originalMethod.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() );
複製程式碼
  1. 類的屬性 大小寫轉換
// 解釋:descriptor.initializer函式由Babel內部使用來建立物件屬性的屬性描述符的值
function 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() );
複製程式碼
  1. 類裝飾器
function withLoginStatus( UserRef ) {
  return class 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 );
複製程式碼

babel 裝換

1、建立專案檔案
2、命令列進入該專案目錄 npm init 
3、npm install babel-core babel-plugin-transform-decorators
4、安裝 npm install babel-plugin-transform-decorators-legacy --save-dev
5、.babelrc新增
{  
  "plugins": [    
    "transform-decorators-legacy"
  ]
}
複製程式碼

參考連結

相關文章