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// 目標屬性是否可以被刪除或是否可以再次修改特性
} );
複製程式碼
應用舉例
- 類方法 @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;
}
複製程式碼
- 類方法 @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() );
複製程式碼
- 類的屬性 大小寫轉換
// 解釋: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() );
複製程式碼
- 類裝飾器
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"
]
}
複製程式碼
參考連結