簡樸相識一下ES6的潤飾器

閑言

一切都要從公司里的一名老哥給我看的一段代碼提及。。。

@controller('/user')

@auth
@post('/login')
async userLogin = (name, pass) => {
    @required
    // ...
}

以下為對話:

我:這不是潤飾器嗎(由於之前看到過@這個東西)

老哥:還不錯嘛,曉得是潤飾器,那你曉得這一段想表達什麼意思嗎

我:這是路由?(一臉懵逼,然則看到了/user和post另有/login,內心想豈非這是路由)

老哥:穩!

我:震動了,還能夠如許寫路由。不可,歸去我要好悅目看這個破@

由此最先了潤飾器的進修~~~
嚶嚶嚶~~

初識Decorator

起首申明,潤飾器在JavaScript中還處於發起階段,現在還不能夠被大部份環境支撐,而且以後另有可能會轉變。假如想要運用該特徵請用Babel舉行轉碼或許運用JavaScript的超集TypeScript

在ES6中增加了類的相干定義和操縱(比方class和extends),如許方便了我們單個類的操縱,然則當我們想要在多個不同類之間同享、復用一些要領的時刻,會發明變得不那麼文雅,所以decorator被提了出來。

小小的demo:以@作為標識符,既能夠作用於類,也能夠作用於類屬性

@decorator
class Cat {}

class Dog {
    @decorator
    run() {}
}

潤飾器的運用

既然decorator與類相干,我們先相識一下類(這裏只是簡樸引見,想要細緻相識的,請自行查閱相干材料,引薦阮一峰的ES6入門)

class Cat {
    constructor(name) {
        this.name = name
    }
    say () {
        console.log("miao ~")
    }
}

這實際上是一個語法糖,詳細的實現是經由過程Object.defineProperty()要領來操縱的,該要領語法以下:

Object.defineProperty(obj, prop, descriptor)

-> obj: 要在其上定義屬性的對象
-> prop: 要定義或修正的屬性的稱號
-> descriptor:要被定義或修正的屬性描述符

返回:通報給該要領的對象(即obj)

所以上面誰人Cat的代碼實際上在實行時是如許的:

function Cat() {}
Object.defineProperty(Cat.prototype, "say", {
    value: function() {console.log("miao ~")}, // 該屬性對應的值
    enumerable: false, // 為true時,才夠出現在對象的羅列屬性中
    configurable: true, // 為true時,該屬性描述符才夠被轉變
    writable: true // 為true時,value才被賦值運算符轉變
}) // 返回Cat.prototype

作用於類的潤飾器

function isAnimal(target) { 
    target.isAnimal = true
    return target // 返回的是通報給該函數的對象
}

@isAnimal
class Cat {
    // ...
}
console.log(Cat.isAnimal) // true

上面的代碼基礎等同於:

Cat = isAnimal(function Cat() {})

作用於類屬性的潤飾器

function readonly(target, name, descriptor) { 
    descriptor.writable = false
    return descriptor
}

class Cat {
    @readonly 
    say () {
        console.log("miao ~")
    }
}

let kitty = new Cat()
kitty.say = function () {
    console.log("wow ~")
}

kitty.say() // miao ~

經由過程將descriptor屬性描述符的writable設置為false,使得say要領只讀,後面臨它舉行的賦值操縱不會見效,挪用的依舊是之前的要領。

有木有以為readonly()要領的參數素昧平生?它和上文引見ES6中的類中提到的Object.defineProperty()是一樣的。

實在潤飾器在作用於屬性的時刻,實際上是經由過程Object.defineProperty舉行擴大和封裝的。所以上面的代碼實際上是如許的:

let descriptor = {
    value: function() {
        console.log("miao ~")
    },
    enumerable: false,
    configurable: true,
    writable: true
}

descriptor = readonly(Cat.prototype, "say", descriptor) || descriptor
Object.defineProperty(Cat.prototype, "say", descriptor)

當潤飾器作用於類時,我們操縱的對象是類自身,當潤飾器作用於類屬性時,我們操縱的對象既不是類自身也不是類屬性,而是它的描述符(descriptor)。

固然了,你也能夠直接在target上舉行擴大和封裝。

function fast(target, name, descriptor) {
    target.speed = 20
    let run = descriptor.value()
    descriptor.value = function() {
        run()
        console.log(`speed ${this.speed}`)
    }
    return descriptor
}

class Rabbit {
    @fast
    run() {
        console.log("running~")
    }
}

let bunny = new Rabbit()

bunny.run() 
// running ~
// speed 20
console.log(bunny.speed) // 20

回到文章最先

讓我們回到文章最先講到的代碼,它怎樣瀏覽呢

@controller("/api/user")

export class userController {
    @post("/add")
    @required({
        body: ["telephone", 'key1']
    })
    async addUser(ctx, next) {}
    
    @get("/userlist")
    async userList(ctx, next) {}
}

讓我們先看@controller("/api/user")

const symbolPrefix = Symbol('prefix')
export const controller = path => target => (target.prototype[symbolPrefix] = path)

作用是將/api/user作為prefixPath,此時target為userController {},在該target的原型上設置path

再接着看@post("/add")

// 以下代碼省略了部份細節
...
// export const post = path => (target, name, descriptor) => {}

routerMap.set({
    target: target,
    ...conf
}, target[name]) // name為addUser
for(let [conf, func] of routerMap) {
    // conf為{target, path} 該target為userController {},path為@post()通報進來的參數
    let prefixPath = conf.target[symbolPrefix] // 為 /api/user
    let routePath = prefix + path // 為 /api/user/add
}
// 獲得了api途徑(routePath),也獲得了該api途徑所實行的要領(func)
// get同理
...

道理基礎上都是相似的,處置懲罰潤飾器通報的參數,獲得本身想要的效果。

額,閱歷過上面的學問相識,應該能也許夠明白這段代碼了吧~

小結

潤飾器許可你在類和要領定義的時刻去解釋或許修正它。潤飾器是一個作用於函數的表達式,它吸收三個參數 target、 name 和 descriptor , 然後可選性的返回被裝潢以後的 descriptor 對象。

你也能夠疊加運用,就像如許

@post("/add")
@required({
    body: ["telephone", 'key1']
})

async xxx {}

參考:

    原文作者:luckyzv
    原文地址: https://segmentfault.com/a/1190000014441855
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞