什麼是裝飾器react
原名decorator 被翻譯爲裝飾器 能夠理解爲裝飾 修飾 包裝等意數據庫
一間房子經過裝飾能夠變得更華麗,功能更多
相似一部手機能夠單獨使用 可是不少人都願意家個保護套來防摔。。。npm
裝飾器能夠說是解決了不一樣類之間共享方法的問題(能夠看作是彌補繼承的不足)。redux
A Python decorator is a function that takes another function, extending the behavior of the latter function without explicitly modifying it.
這句話能夠說是對裝飾器的很是漂亮的解釋。api
在將來的 JavaScript 中也引入了這個概念,而且 babel 對他有很好的支持。若是你是一個瘋狂的開發者,就能夠藉助 babel 大膽使用它。瀏覽器
裝飾器目前在瀏覽器或者 Node 中都暫時不支持,須要藉助 babel 轉化爲可支持的版本babel
安裝 babelapp
按照官網的 說明 安裝:koa
npm install --save-dev babel-cli babel-preset-env
在 .babelrc 中寫入:async
{ "presets": ["env"] }
按照說明,安裝 babel-plugin-transform-decorators-legacy 插件:
npm install babel-plugin-transform-decorators-legacy --save-dev
.babelrc :
{ "presets": ["env"], "plugins": ["transform-decorators-legacy"] }
這樣準備工做就完成了。
先看看一個裝飾器的寫法
class Boy{ @run speak (){ console.log('I can speak') } } function run () { console.log('I can run') } let tj = new Boy() tj.speak() // I can run // I can speak
@run 就是給類屬性方法(speak)加的一個裝飾器(其實也就是一個函數) 擴展了類Boy的speak(在講話的同時跑步)
裝飾器不只能夠裝飾類的方法還能夠裝飾類(可是不能夠裝飾函數,由於函數存在變量提高)
裝飾器函數接受3個參數 分別是裝飾的對象,裝飾的屬性,裝飾屬性的描述
class Boy{ @run speak (){ console.log('I can speak') } } function run (target,key,descripter) { console.log(target,key,descripter) } let tj = new Boy() tj.speak() // Boy {} 'speak' { value: [Function: speak], writable: true, enumerable: false, configurable: true } I can speak
再來看一個例子
class Math { @log add(a, b) { return a + b } } function log(target, name, descriptor) { var oldValue = descriptor.value descriptor.value = function() { console.log(`Calling ${name} with`, arguments) return oldValue.apply(target, arguments) } return descriptor } const math = new Math() // passed parameters should get logged now math.add(2, 4) // Calling add with { '0': 2, '1': 4 }
至關於在原來的add方法上擴展了一個console.log的功能,並無改變原來的功能 (咱們能夠取到參數 並改變他)
還能夠經過裝飾器傳遞參數
function log(num) { return function(target, name, descriptor) { var oldValue = descriptor.value let _num = num || 0 descriptor.value = (...arg) => { arg[0] += _num console.log(`Calling${target}, ${name} with`, arg) return oldValue.apply(target, arg) } return descriptor } } class Math { constructor(a = 3, b = 4) { this.add(a, b) } @log(100) add(a, b) { return a + b } } const math = new Math() console.log(math) console.log(math.add(9,1))
咱們想給koa-router擴展更多的功能,而且是可讀性維護性和代碼的優雅性都很好的好比:
export default class MovieRouter{ @get('/api/v0/movie') @auth() @log() ... }
讓路由在真正處理業務的時候先作些其餘的準備工做(如上先驗證用戶是否登陸,而後輸出日誌)
就以上,咱們先簡單實現一下
const Koa = require('koa') const app = new Koa() const {connect} = require('../db/index') const mongoose = require('mongoose') const Shijue = mongoose.model('Shijue') const Router = require('koa-router') const router = new Router() // 鏈接數據庫 void (async () => { await connect() })() class Route { constructor() { this.app = app this.router = router } init() { routerMap.map(item=>{ router[item.method](item.path, item.callback) }) app.use(router.routes()) app.use(router.allowedMethods()) } } var routerMap = [] function get(path) { return function(target, key, descriptor) { routerMap.push({path, target, method: 'get', callback: target[key]}) return descriptor } } var logTimes = 0 function log() { return function(target, key, descriptor) { app.use(async function(ctx, next) { logTimes++ console.time(`${logTimes}: ${ctx.method} - ${ctx.url}`) await next() console.timeEnd(`${logTimes}: ${ctx.method} - ${ctx.url}`) return descriptor }) } } class ShijueRouter { @get('/api') @log() async getShijue(ctx, next) { // await ... return (ctx.body = {code: 0, data: 'shijue'}) } } app.use(router.routes()) app.use(router.allowedMethods()) async function start() { var r = new Route() r.init() app.listen(3001, function(err) { if (err) { console.log(err) } else { console.log('啓動成功:3001') } }) } start()
代碼比較粗糙能夠提煉分離
還有如react-redux的實現等