修飾器(Decorator)是ES7的一個提案,它的出現能解決兩個問題:vue
用法也很簡單,就是在類或方法的上面加一個@符,在vue in typescript
中常常用到webpack
以上的兩個用處可能不太明白,不要緊,咱們開始第一個例子git
@setProp class User {} function setProp(target) { target.age = 30 } console.log(User.age)
這個例子要表達的是對User
類使用setProp
這個方法進行修飾,用來增長User
類中age
的屬性,setProp
方法會接收3個參數,咱們如今接觸第一個,target
表明User
類自己。github
@setProp(20) class User {} function setProp(value) { return function (target) { target.age = value } } console.log(User.age)
此例和上面功能基本一致,惟一差異在於值是參考修飾函數傳過來的web
class User { @readonly getName() { return 'Hello World' } } // readonly修飾函數,對方法進行只讀操做 function readonly(target, name, descriptor) { descriptor.writable = false return descriptor } let u = new User() // 嘗試修改函數,在控制檯會報錯 u.getName = () => { return 'I will override' }
上例中,咱們對User
類中的getName
方法使用readonly
修飾器進行修飾,使得方法不能被修改。第一個參數咱們已經知道了,參數name
爲方法名,也就是readonly
,參數descriptor
是個啥東西呢,看到這行descriptor.writable = false
,咱們你們猜的也差很少了,這三個參數對應的就是Object.defineProperty
的三個參數,咱們來看一下:typescript
咱們設置descriptor.writable = false
就是讓函數不能夠被修改,若是咱們寫成編程
descriptor.value = 'function (){ console.log('Hello decorator') }'
那麼,輸出就是Hello World
了,而是Hello decorator
,是否是已經意識到修飾器的好處了。如今咱們來看看實際工做中,咱們用到修飾器的例子json
在用webpack
打包時,咱們常常須要好多步驟,好比第一步讀取package.json
文件,第二步處理該文件,第三步加載webpack.base.js
文件,第四步進行打包...爲了直觀,咱們常常在每一步打印一些日誌文件,好比這步都幹了些什麼事,很明顯打印日誌的操做和業務代碼根本就一點關係沒有,咱們不該該把日誌和業務摻和在一塊兒,這樣使用修飾器就是避免這個問題,如下爲代碼:app
class Pack { @log('讀取package.json文件') step1() { // do something... // 沒有修飾器以前,咱們一般把console.log放到這裏寫 // 放到函數裏面寫會有兩個壞處 // 1.console和業務無關,會破壞函數單一性原則 // 2.若是要刪除全部的console,那咱們只能深刻到每個方法中 } @log('合併webpack配置文件') step2() { // do something... } } function log(value) { return function (target, name, descriptor) { // 在這裏,咱們還能夠拿到函數的參數,打印更加詳細的信息 console.log(value) } } let pack = new Pack() pack.step1() pack.step2()
這個例子在實際的開發中經常使用獲得,咱們一些操做前,必須得判斷用戶是否登陸,比較點贊、結算、發送彈幕...按照以前的寫法,咱們必須在每個方法中判斷用戶的登陸狀況,而後再進行業務的操做,很顯然前置條件和業務又混到了一塊兒,用修飾器,就能夠完美的解決這一問題,代碼以下:ide
class User { // 獲取已登陸用戶的用戶信息 @checkLogin getUserInfo() { /** * 以前,咱們都會這麼寫: * if(checkLogin()) { * // 業務代碼 * } * 這段代碼會在每個須要登陸的方法中執行 * 仍是上面的問題,執行的前提和業務又混到了一塊兒 */ console.log('獲取已登陸用戶的用戶信息') } // 發送消息 @checkLogin sendMsg() { console.log('發送消息') } } // 檢查用戶是否登陸,若是沒有登陸,就跳轉到登陸頁面 function checkLogin(target, name, descriptor) { let method = descriptor.value // 模擬判斷條件 let isLogin = true descriptor.value = function (...args) { if (isLogin) { method.apply(this, args) } else { console.log('沒有登陸,即將跳轉到登陸頁面...') } } } let u = new User() u.getUserInfo() u.sendMsg()
以上只是修飾器的基本應用,只要咱們掌握了原理,在實際的工做中,要思考本身的應用場景,只要咱們涉及須要在執行前作一些處理的應用,不論是修改函數的參數值,仍是增長屬性,仍是執行的先決條件,咱們均可以使用修飾器,這種編程的方式,就是面向切面編程