成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

ES6的一個(gè)基礎(chǔ)類,支持私有屬性和方法,支持event和mix

Jeffrrey / 3246人閱讀

摘要:綁定事件,傳入一個(gè)回調(diào)函數(shù),但是這里還給加了一個(gè)功能,就是第三個(gè)參數(shù)規(guī)定回調(diào)函數(shù)執(zhí)行的順序。比如當(dāng)你給同一個(gè)事件傳入了多個(gè)回調(diào)函數(shù),怎么來(lái)規(guī)定它們之間的順序呢通過(guò)傳入第三個(gè)參數(shù)即可,數(shù)字越小的,越靠前執(zhí)行。

ES6提供了完整的class語(yǔ)法,因此,可以非常方便的使用extends關(guān)鍵字對(duì)類進(jìn)行擴(kuò)展(繼承)。為了實(shí)現(xiàn)類的一些基礎(chǔ)功能,我撰寫了下面這個(gè)類,用以被其他類繼承,擁有這個(gè)基礎(chǔ)類的基礎(chǔ)功能。

源碼
var events = {}
var data = {}
var copyProperty = function(Target, Source) {
    for(let key of Reflect.ownKeys(Source)) {
        if(key !== "constructor" && key !== "prototype" && key !== "name") {
            let descriptor = Object.getOwnPropertyDescriptor(Source, key)
            Object.defineProperty(Target, key, descriptor)
        }
    }
}

export default class ClassBase {
    constructor(...args) {
        events[this] = {}
        data[this] = {}
        this.call(this.initialize, ...args)

        return this
    }

    /**
     * @desc initialize class method, be called every time class is initialized
     * Notice: never use constructor when extends by a sub class
     */
    initialize() {}

    /**
     * @desc get data from data manager
     * @param string key: the key of data, you can use "." to get tree info. e.g. .get("root.sub.ClassBaseMix") => .get("root").sub.ClassBaseMix
     */
    get(key) {
        let target = data[this]
        if(key.indexOf(".") === -1) return target[key]

        let nodes = key.split(".").filter(item => item && item !== "")
        if(nodes.length === 0) return

        for(let node of nodes) {
            if(typeof target !== "object" || !target[node]) return
            target = target[node]
        }

        return target
    }
    /**
     * @desc save data to data manager
     * @param string key: the key of data, use "." to set tree structure. e.g. .set("root.sub.ClassBaseMix", "value") => .get("root").sub.ClassBaseMix = "value"
     * @param mix value: the value to save
     * @param boolean notify: whether to trigger data change event
     */
    set(key, value, notify = true) {
        if(!data[this]) data[this] = {}
        let target = data[this]
        if(key.indexOf(".") === -1) {
            target[key] = value
            if(notify) {
                this.trigger("change:" + key, value)
            }
            return this
        }

        let nodes = key.split(".").filter(item => item && item !== "")
        if(nodes.length === 0) return

        let lastKey = nodes.pop()
        for(let node of nodes) {
            if(typeof target !== "object") return
            if(!target[node]) {
                target[node] = {}
            }
            target = target[node]
        }
        target[lastKey] = value

        if(notify) {
            nodes.push(lastKey)
            let event = nodes.shift()
            this.trigger("change:" + event, value)
            while (nodes.length > 0) {
                event += "." + nodes.shift()
                this.trigger("change:" + event, value)
            }
        }

        return this
    }

    /**
     * @desc call some function out of this class bind with this
     * @param function factory: the function to call
     * @param args: arguments to pass to function be called
     */
    call(factory, ...args) {
        factory.apply(this, args)
        return this
    }

    /**
     * @desc bind events on Instantiate objects
     * @param string evts: events want to bind, use " " to split different events, e.g. .on("change:data change:name", ...)
     * @param function handler: function to call back when event triggered
     * @param number order: the order to call function. functions are listed one by one with using order.
     */
    on(evts, handler, order = 10) {
        if(!events[this]) events[this] = {}
        evts = evts.split(" ")
        let target = events[this]

        evts.forEach(evt => {
            if(!target[evt]) {
                target[evt] = {}
            }
            let node = target[evt]

            if(!node[order]) node[order] = []
            let hdles = node[order]
            if(hdles.indexOf(handler) === -1) hdles.push(handler) // make sure only once in one order
        })

        return this
    }
    /**
     * @desc remove event handlers
     * @param string event: event name, only one event supported
     * @param function handler: the function wanted to remove, notice: if you passed it twice, all of them will be removed. If you do not pass handler, all handlers of this event will be removed.
     */
    off(event, handler) {
        if(!handler) {
            events[this][event] = {}
            return
        }

        let node = events[this][event]
        if(!node) return

        let orders = Object.keys(node)

        if(!orders || orders.length === 0) return
        if(orders.length > 1) orders = orders.sort((a, b) => a - b)

        orders.forEach(order => {
            let hdles = node[order]
            let index = hdles.indexOf(handler)
            if(index > -1) hdles.splice(index, 1) // delete it/them
            if(hdles.length === 0) delete node[order]
        })

        return this
    }
    /**
     * @desc trigger events handlers
     * @param string event: which event to trigger
     * @param args: arguments to pass to handler function
     */
    trigger(event, ...args) {
        let node = events[this][event]
        if(!node) return

        let orders = Object.keys(node)

        if(!orders || orders.length === 0) return
        if(orders.length > 1) orders = orders.sort((a, b) => a - b)

        let handlers = []
        orders.forEach(order => {
            let hdles = node[order]
            handlers = [...handlers, ...hdles]
        })

        handlers.forEach(handler => {
            if(typeof handler === "function") {
                // this.call(handler, ...args) // 會(huì)綁定this
                handler(...args) // 不會(huì)綁定this,其實(shí)可以在on的時(shí)候用bind去綁定
            }
        })

        return this
    }

    /**
     * @desc mix this class with other classes, this class property will never be overwrite, the final output class contains certain property and all of this class"s property
     * @param Classes: the classes passed to mix, previous class will NOT be overwrite by the behind ones.
     */
    static mixin(...Classes) {
        class ClassBaseMix {}

        Classes.reverse()
        Classes.push(this)
        for(let Mixin of Classes) {
            copyProperty(ClassBaseMix, Mixin)
            copyProperty(ClassBaseMix.prototype, Mixin.prototype)
        }

        return ClassBaseMix
    }
    /**
     * @desc mix other classes into this class, property may be overwrite by passed class, behind class will cover previous class
     */
    static mixto(...Classes) {
        class ClassBaseMix {}

        Classes.unshift(this)
        for(let Mixin of Classes) {
            copyProperty(ClassBaseMix, Mixin)
            copyProperty(ClassBaseMix.prototype, Mixin.prototype)
        }

        return ClassBaseMix
    }

    toString() {
        return this.constructor.name
    }
}

你可以在這里閱讀每一個(gè)方法的說(shuō)明,這里簡(jiǎn)單的說(shuō)明一下它們的各自用途。

initialize方法

用來(lái)替代constructor作為實(shí)例化方法,雖然在class中使用constructor并沒(méi)有什么問(wèn)題,但是大家似乎約定熟成的使用initialize方法替換它。所以constructor方法作為一個(gè)最起始的方法,不應(yīng)該在子類中出現(xiàn)被覆蓋,因?yàn)檫@里會(huì)用它來(lái)調(diào)用initialize方法,一旦被覆蓋,子類中就不能自動(dòng)調(diào)用initialize方法了。

setter和getter

用以獲取和設(shè)置私有屬性,當(dāng)然也可以保存其他任何數(shù)據(jù)類型。這些數(shù)據(jù)都會(huì)被保存在attributions這個(gè)變量里面,但是它僅在這個(gè)文檔中可見,所以不會(huì)被外部訪問(wèn),只有通過(guò)get和set方法才能訪問(wèn)。

而且功能有所提升,傳入的變量支持用點(diǎn)隔開來(lái)表示父子關(guān)系。比如set("book.name", "News"),這樣可以直接設(shè)置book對(duì)象的name屬性,用get("book").name = "News"也可以達(dá)到這個(gè)效果(性能更高),但形式上沒(méi)有前者好看,使用get("book.name")這種寫法也更優(yōu)雅。

on、off和trigger

和大多數(shù)事件綁定和觸發(fā)一樣,這三個(gè)方法也是實(shí)現(xiàn)這個(gè)功能的。on綁定事件,傳入一個(gè)回調(diào)函數(shù),但是這里還給on加了一個(gè)功能,就是第三個(gè)參數(shù)規(guī)定回調(diào)函數(shù)執(zhí)行的順序。比如當(dāng)你給同一個(gè)事件傳入了多個(gè)回調(diào)函數(shù),怎么來(lái)規(guī)定它們之間的順序呢?通過(guò)傳入第三個(gè)參數(shù)即可,數(shù)字越小的,越靠前執(zhí)行。

off在解綁事件的時(shí)候,也有一個(gè)比較好的功能,可以只解綁某一個(gè)回調(diào)函數(shù),但前提是,你在on的時(shí)候,傳入的是變量名函數(shù),解綁的時(shí)候也是這個(gè)指向函數(shù)的變量。

setter中的事件

當(dāng)你使用set方法設(shè)置一個(gè)新值的時(shí)候,這個(gè)類會(huì)自動(dòng)調(diào)用trigger方法去觸發(fā)一個(gè)change事件,例如set("name", "new name")的時(shí)候trigger("change:name", "new name")會(huì)被自動(dòng)觸發(fā)。這和backbone的規(guī)則非常像。

同時(shí),像getter,setter里面的層級(jí)關(guān)系也被支持了,比如set("book.name", "Book"),這個(gè)時(shí)候其實(shí)觸發(fā)了兩個(gè)事件,一個(gè)是change:book,一個(gè)是change:book.name,它們都會(huì)被觸發(fā),而且是兩個(gè)獨(dú)立的事件,綁在change:book上的回調(diào)函數(shù)和綁在change:book.name上的回調(diào)函數(shù)是完全分開的,沒(méi)有任何關(guān)系,change:book事件的回調(diào)函數(shù)會(huì)被先執(zhí)行。如果你不想使用這個(gè)功能,可以把set方法的第三個(gè)參數(shù)設(shè)置為false,這樣就不會(huì)觸發(fā)trigger。

call私有方法

ES還不支持private關(guān)鍵字,所以不能直接定義私有方法。這個(gè)類擁有一個(gè).call方法,可以用來(lái)調(diào)用私有方法。私有方法寫在class外面,跟attributions、events這兩個(gè)變量差不多。但是在私有方法里面可以使用this,以及this攜帶的任何東西,在class里面call它的時(shí)候,this都是有效的。

mix一次性繼承多個(gè)類

在backbone或其他一些框架中,每個(gè)類有一個(gè)extends方法來(lái)創(chuàng)建一個(gè)子類,在extends參數(shù)中寫方法來(lái)覆蓋父類的方法。但是這種操作只能繼承于一個(gè)類,而如果想一次性繼承幾個(gè)類的某些方法,還需要自己寫個(gè)擴(kuò)展方法來(lái)實(shí)現(xiàn)。再說(shuō)了,ES6本身就提供了extends關(guān)鍵字來(lái)繼承類,所以單純的extends方法不應(yīng)該再繼續(xù)使用了,只需要寫一個(gè)mix方法來(lái)混入這些想要繼承的類就可以了。

我寫的這個(gè)類提供了兩個(gè)方法,mixin和mixto,混入的方式不同。mixin是把參數(shù)里面的類的方法或?qū)傩砸粋€(gè)一個(gè)往自己里面塞,而mixto是把自己的方法或?qū)傩酝鶇?shù)里面的類塞,方向上正好相反。由于塞的方向不同,最終如果方法有重名的話,被塞的一方的方法就會(huì)被保留下來(lái),作為最終產(chǎn)生的混類的主體。寫一個(gè)繼承就非常簡(jiǎn)單:

class SubClass extends ClassBase.mixin(A, B, C) {}

mixin和mixto都是靜態(tài)屬性,所以可以直接用類名來(lái)調(diào)用。

toString獲取類名

有的時(shí)候你想看下當(dāng)前的實(shí)例化對(duì)象到底是從哪個(gè)類實(shí)例化出來(lái)的,那么直接用toString方法來(lái)獲取類的名稱。原本我想返回"[class BaseClass]"這種類型的字符串,但是絕對(duì)沒(méi)什么意義,還不如直接返回類名,還可以用來(lái)做比較。

本文發(fā)布在我的博客
求個(gè)兼職,如果您有web開發(fā)方面的需要,可以聯(lián)系我,生活不容易,且行且珍惜。
請(qǐng)?jiān)谖业膫€(gè)人博客 www.tangshuang.net 留言,我會(huì)聯(lián)系你。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86941.html

相關(guān)文章

  • ES6中class私有屬性私有方法

    摘要:新增的語(yǔ)法非常帥,但是圍繞這個(gè)新的語(yǔ)法糖,在中如何實(shí)現(xiàn)靜態(tài)屬性私有屬性私有方法的問(wèn)題,成為了大家探討的話題。私有方法理論上講,私有屬性和私有方法的區(qū)別是,私有方法是函數(shù)。 ES6新增的class語(yǔ)法非常帥,但是圍繞這個(gè)新的語(yǔ)法糖,在class中如何實(shí)現(xiàn)靜態(tài)屬性、私有屬性、私有方法的問(wèn)題,成為了大家探討的話題。本文打算繞過(guò)現(xiàn)有的weakmap、symbol的方案,從最簡(jiǎn)單的實(shí)踐中抽取出滿...

    bergwhite 評(píng)論0 收藏0
  • ES6中文手冊(cè)、ES6 Cheatsheet

    摘要:盲目使用替換后可能會(huì)導(dǎo)致預(yù)期意外的結(jié)果。在中,許多種方法來(lái)處理函數(shù)的參數(shù)默認(rèn)值,參數(shù)數(shù)量,參數(shù)命名。此外,處理后的值,無(wú)論是解決還是拒絕的結(jié)果值,都是不可改變的。 這是一個(gè) ES2015(ES6) 的Cheatsheet,其中包括提示、小技巧、最佳實(shí)踐和一些代碼片段,幫助你完成日復(fù)一日的開發(fā)工作。 Table of Contents var 與 let / const 聲明 代碼執(zhí)行...

    Cristalven 評(píng)論0 收藏0
  • 從0到1使用VUE-CLI3開發(fā)實(shí)戰(zhàn)(三): ES6/ES7知識(shí)儲(chǔ)備

    摘要:它們都用于聲明變量。盲目使用替換后可能會(huì)導(dǎo)致預(yù)期意外的結(jié)果。有鑒于此,還是建議使用字符串,布爾和數(shù)字類型的數(shù)據(jù)類型。像使用這種下劃線命名約定在一個(gè)開源項(xiàng)目中,命名規(guī)則很難維持得一直很好,這樣經(jīng)常會(huì)造成一些困擾。 今天群里有小伙伴跟我聊天,問(wèn)了我?guī)讉€(gè)關(guān)于ES6的問(wèn)題,我才意識(shí)到,大部分初學(xué)者在學(xué)習(xí)的過(guò)程中,都是學(xué)了HTML/CSS/JS之后就開始上手學(xué)習(xí)框架了,而對(duì)于ES6的重視程度卻不...

    crossoverJie 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<