欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中三種觀察者實(shí)現(xiàn)案例分享

 更新時(shí)間:2023年08月08日 08:39:40   作者:剪刀石頭布啊  
前面突然看到 Object.defineProperty,就順道想到 Proxy,然后就想到了觀察者案例,這邊還沒(méi)有用 javascript編寫一個(gè)觀察者的案例呢,順道加入了一個(gè) event-bus 監(jiān)聽(tīng)事件案例,湊一起看一看不同的實(shí)現(xiàn)方式,需要的朋友可以參考下

event-bus

event-bus最基礎(chǔ)的事件監(jiān)聽(tīng),算是一個(gè)通知類型的,可以設(shè)置成一個(gè)單例全局使用,也可以用在局部

通過(guò)名字訂閱監(jiān)聽(tīng),通過(guò)名字發(fā)布新消息,使用也比較簡(jiǎn)單

export default class EventBus {
    events: Record<string, Array<Function>> = {}
    //訂閱
    subscribe(name: string, callback: Function) {
        if (!this.events[name]) {
            this.events[name] = []
        }
        this.events[name].push(callback)
    }
    //發(fā)布 -- data是往call中傳遞參數(shù)以便于使用的
    publish(name: string, data?: any) {
        let event = this.events[name]
        if (event) {
            event.forEach(callback => callback(data))
        }
    }
    //取消訂閱,y
    unsubscribe(name: string, callback?: Function) {
        let event = this.events[name]
        if (!callback) {
            this.events[name] = []
        }else if (event) {
            this.events[name] = event.filter(e => e !== callback)
        }
    }
}

測(cè)試案例

const event = new EventBus()
const subscribe = () => {
    event.subscribe('name', (value: any) => {
        console.log(value)
    })
    console.log('已訂閱')
}
const publish = () => {
    let content = "我發(fā)布消息了,內(nèi)容是\"哈啊哈哈\""
    event.publish('name', content)
}
const unsubscribe = () => {
    event.unsubscribe('name')
    console.log('取消訂閱了')
}

自動(dòng)銷毀觀察者(event-bus版)

這里根據(jù) event-bus 改變,參考 UI組件生命周期 變化,實(shí)現(xiàn)UI組件銷毀時(shí)自動(dòng)釋放的監(jiān)聽(tīng)訂閱案例,實(shí)際也就多了一個(gè)上下文,方便統(tǒng)一銷毀罷了

///------自動(dòng)釋放組件----
//假設(shè)組件釋放調(diào)用方法為onDestory
const __ComponentDestoryName = 'onDestory'
class EventObj {
    key: string
    fn: Function[] = []
    constructor(key: string, fn: Function) {
        this.key = key
        this.fn.push(fn)
    }
}
class ContextEventObj<T = EventObj> {
    context: any
    events: T[] = []
    constructor(context: any, event: T) {
        this.context = context
        this.events.push(event)
    }
    //假設(shè)組件釋放調(diào)用方法為destory
    registerDestory(callback: Function) {
        let desFunc = this.context[__ComponentDestoryName]
        this.context[__ComponentDestoryName] = function() {
            callback()
            desFunc()
        }
    }
}
//根據(jù)上下文context自動(dòng)釋放版本(
class EventBusByAutoRelease {
    events: ContextEventObj[] = []
    //訂閱,context為所屬上下文,當(dāng)上下文對(duì)象銷毀時(shí),響應(yīng)監(jiān)聽(tīng)隨機(jī)銷毀
    subscribe(name: string, callback: Function, context: any = null) {
        let contextObj = this.events.find(e => e.context === context)
        if (contextObj) {
            let event = contextObj.events.find(e => e.key === name)
            event?.fn.push(callback)
        }else {
            let event = new EventObj(name, callback)
            contextObj = new ContextEventObj(context, event)
            contextObj.registerDestory(() => {
                this.events = this.events.filter(e => e.context !== context)
            })
            this.events.push(contextObj)
        }
    }
    //發(fā)布 -- data是往call中傳遞參數(shù)以便于使用的
    publish(name: string, data?: any) {
        this.events.forEach(item => 
            item.events.forEach(e =>  
                e.key === name && 
                e.fn.forEach(fn => fn(data))
            )
        )
    }
    //取消訂閱
    unsubscribe(context?: any, name?: string) {
        if (context) {
            let ctx = this.events.find(e => e.context === context)
            if (!ctx) return
            if (name) {
                ctx.events = ctx.events.filter(e => e.key !== name)
            }else {
                this.events = this.events.filter(e => e.context !== context)
            }
        }else {
            this.events.length = 0
        }
    }
}

observer

這個(gè)觀察者模式是通過(guò)Object.defineProperty方法,沖定義指定對(duì)象的 set、get 方法以實(shí)現(xiàn)自動(dòng)監(jiān)聽(tīng)的效果,實(shí)現(xiàn)方法參考了一些以前看過(guò)的其他平臺(tái)的部分實(shí)現(xiàn)案例(實(shí)際上實(shí)現(xiàn)可以更簡(jiǎn)潔)

ps:自己也可以嘗試寫一個(gè)更好用的哈,這一主要是應(yīng)用 Object.defineProperty

const symbol_observe = '__obs_map'
const canObserve = (obj: any) => obj !== null && typeof obj === 'object'
class ObserveObj {
    target: any
    key: string
    fn: Function[] = []
    val: any
    constructor(target: any, key: string, fn: Function, val: any) {
        this.target = target
        this.key = key
        this.fn.push(fn)
        this.val = val
        this.defineProperty()
    }
    defineProperty() {
        let that = this
        Object.defineProperty(that.target, that.key, {
            get() {
                return that.val
            },
            set(newVal) {
                that.notify(newVal)
            },
            enumerable: true, // 可枚舉
        })
    }
    notify(val: any) {
        this.val = val
        this.fn.forEach(e => e(val))
    }
    removeObserver() {
        delete this.target[this.key]
        this.target = null
    }
}
class TargetObs {
    target: any
    observes: Array<Record<'key' | 'fn', string | Function>> = []
    constructor(target: any) {
        this.target = target
    }
}
export default class Observer {
    targets: TargetObs[] = [] //用于保存target和callback信息,以便于后續(xù)清理
    observe(target: any, key: string, callback: Function) {
        if (!canObserve(target)) return
        let targetObs = this.targets.find(e => e.target === target)
        if (!targetObs) {
            targetObs = new TargetObs(target)
            targetObs.observes.push({
                key,
                fn: callback
            })
            this.targets.push(targetObs)
        }
        if (!target[symbol_observe]) {
            target[symbol_observe] = new Map<string, ObserveObj>()
        }
        let observeMap: Map<string, ObserveObj> = target[symbol_observe]
        let observeObj = observeMap.get(key)
        if (observeObj) {
            observeObj.fn.push(callback)
        }else {
            observeObj = new ObserveObj(target, key, callback, target[key])
            observeMap.set(key, observeObj)
        }
    }
    //清理某個(gè)target部分監(jiān)聽(tīng)
    unobserve(target: any, key: string, callback?: Function) {
        if (!canObserve(target)) return
        let observeMap: Map<string, ObserveObj> = target[symbol_observe]
        if (!observeMap) return
        let obj = observeMap.get(key)
        if (!obj) return
        let targetObs = this.targets.find(e => e.target = target)
        if (callback) {
            if (targetObs) {
                targetObs.observes = targetObs.observes.filter(e => e.fn !== callback)
            }
            obj.fn = obj.fn.filter(e => e !== callback)
        }else {
            let fns
            if (targetObs) {
                fns = targetObs.observes.filter(e => e.key === key)
            }
            fns?.forEach(item => {
                obj!.fn = obj!.fn.filter(e => e !== item.fn)
            })
        }
        if (obj.fn.length < 1) {
            obj.removeObserver()
            observeMap.delete(key)
        }
    }
    //清理本觀察者添加的指定監(jiān)聽(tīng)
    unobserveTarget(target: any) {
        if (!canObserve(target)) return
        let observeMap: Map<string, ObserveObj> = target[symbol_observe]
        if (!observeMap) return
        let targetObs = this.targets.find(e => e.target = target)
        for (let key in observeMap) {
            let fns
            if (targetObs) {
                fns = targetObs.observes.filter(e => e.key === key)
            }
            let obj = observeMap.get(key)
            if (!obj) continue
            fns?.forEach(item => {
                obj!.fn = obj!.fn.filter(e => e !== item.fn)
            })
            if (obj.fn.length < 1) {
                obj.removeObserver()
                observeMap.delete(key)
            }
        }
    }  
    //清理本觀察者添加的指定監(jiān)聽(tīng)
    unobserveAll(target?: any) {
        if (target) {
            this.unobserveTarget(target)
        }else {
            this.targets.forEach(e => {
                this.unobserveTarget(e.target)
            })
            this.targets.length = 0
        }
    }
}

測(cè)試案例

const event = new Observer()
const subscribe = () => {
    event.observe(dataModel, 'name', (value: any) => {
        console.log(value)
    })
    console.log('已訂閱')
}
const publish = () => {
    let content = "我更改內(nèi)容了,內(nèi)容是\"哈啊哈哈\""
    dataModel.name = content
}
const unsubscribe = () => {
    event.unobserve(dataModel, 'name')
    console.log('取消觀察了')
}

observer-proxy

本觀察案例通過(guò) Proxy 代理實(shí)現(xiàn)的,使用了里面的 Proxy.revocable Proxy基礎(chǔ)參考文檔,可以取消監(jiān)聽(tīng)

唯一缺陷更改方法需要使用新的 Proxy 對(duì)象來(lái)代替之前的,否則無(wú)法實(shí)現(xiàn)監(jiān)聽(tīng)功能,實(shí)際使用不是很友好,這里面已經(jīng)盡量讓其更加又友好了??

ps:實(shí)際上 proxy 用在其他特殊場(chǎng)景才能發(fā)揮出其巨大的優(yōu)勢(shì),這里面也是寫出一種實(shí)現(xiàn)方案罷了,有更好的可以討論哈

class ObserveObj {
    observer: string
    key: string
    fn: Function[] = []
    constructor(key: string, observer: any, fn: Function) {
        this.key = key
        this.observer = observer
        this.fn.push(fn)
    }
}
export default class ObserverProxy<T extends Object> {
    target: any //用于回退原來(lái)對(duì)象  ori = proxy.target
    o: T
    rk: Function //用于取消代理
    //存放回調(diào)的數(shù)組,一個(gè)對(duì)象的監(jiān)聽(tīng)一般不會(huì)過(guò)多(例如成百上千),因?yàn)樗嬖谛阅芸梢钥紤]調(diào)整
    observers: ObserveObj[] = []
    //如果根據(jù)observer自動(dòng)釋放,可以重寫observer的銷毀屬性,在哪里自動(dòng)釋放即可,由于很多組件的銷毀方法不一樣就不實(shí)現(xiàn)了
    constructor(target: T) {
        this.target = target
        let that = this
        const {proxy, revoke} = Proxy.revocable(target, {
            //注意里面的 this 都會(huì)指向 target
            get: function (target, propKey, receiver) {
                return Reflect.get(target, propKey, receiver);
            },
            //這里攔截set方法即可
            set: function (target: any, propKey: string, value: any, receiver: any) {
                let observers = that.observers.filter(e => e.key === propKey)
                observers.forEach(e => e.fn.forEach(fn => fn(value)))
                //調(diào)動(dòng)原來(lái)的set方法賦值
                return Reflect.set(target, propKey, value, receiver);
            },
        })
        this.o = proxy
        this.rk = revoke
    }
    revoke() {
        this.rk()
        this.removeObserve()
        let target = this.target
        this.target = null
        return target
    }
    //callback回調(diào),context觀察者(參與觀察的對(duì)象,即回調(diào)所在的類),responseByFirst初次是否響應(yīng)回調(diào)
    addObserve(key: string, callback: Function, observer: any, responseByFirst = false) {
        let observerObj = this.observers.find(
            e => e.observer === observer && e.key === key
        )
        if (!observerObj) {
            observerObj = new ObserveObj(key, observer, callback)
            this.observers.push(observerObj)
        }else {
            observerObj.fn.push(callback)
        }
        responseByFirst && callback(this.target[key])
    }
    removeObserve(observer?: any, key?: string) {
        if (observer) {
            if (key) {
                this.observers = this.observers.filter(e => e.observer !== observer || e.key !== key)
            }else {
                this.observers = this.observers.filter(e => e.observer !== observer)
            }
        }else {
            this.observers.length = 0
        }
    }
}

測(cè)試案例

const dataModel = new ObserverProxy(new DataModel())
const subscribe = () => {
    dataModel.addObserve('name', (value: any) => {
        console.log(value)
    }, this)
    console.log('已訂閱')
}
const publish = () => {
    let content = "我更改內(nèi)容了,內(nèi)容是\"哈啊哈哈\""
    //由于要用代理修改,使用.ob代替原對(duì)象即可
    dataModel.o.name = content
}
const unsubscribe = () => {
    //撤銷監(jiān)聽(tīng)后,返回原對(duì)象,注意原對(duì)象沒(méi)有監(jiān)聽(tīng)
    let res = dataModel.revoke()
    console.log('取消觀察了')
}

最后

最后兩個(gè)本來(lái)想實(shí)現(xiàn)自動(dòng)釋放,由于一些平臺(tái)存在引用計(jì)數(shù)(不只是垃圾回收),引用會(huì)存在內(nèi)存泄露問(wèn)題,

由于 js 對(duì)象沒(méi)有析構(gòu)函數(shù),這里即使加上 WeakMap 也不行,因?yàn)樗豢杀闅v,所以目前對(duì)于我來(lái)說(shuō)實(shí)現(xiàn)自動(dòng)釋放的監(jiān)聽(tīng)尚有困難(當(dāng)然難不住大佬),當(dāng)然這也是后續(xù)要解決的難題,大家要是有策略可以探討,大家一起提升??

以上就是JavaScript三種觀察者實(shí)現(xiàn)案例分享的詳細(xì)內(nèi)容,更多關(guān)于JavaScript觀察者實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • javascript漢字拼音互轉(zhuǎn)的簡(jiǎn)單實(shí)例

    javascript漢字拼音互轉(zhuǎn)的簡(jiǎn)單實(shí)例

    下面小編就為大家?guī)?lái)一篇javascript漢字拼音互轉(zhuǎn)的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-10-10
  • js原生方法被覆蓋,從新賦值原生的方法

    js原生方法被覆蓋,從新賦值原生的方法

    下面小編就為大家分享一篇js原生方法被覆蓋,從新賦值原生的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • JavaScript中BOM,DOM和事件的用法詳解

    JavaScript中BOM,DOM和事件的用法詳解

    BOM全稱Browser Object Model瀏覽器對(duì)象模型,將瀏覽器的各個(gè)組成部分封裝成對(duì)象。DOM全稱Document Object Model 文檔對(duì)象模型,將標(biāo)記語(yǔ)言文檔的各個(gè)組成部分,封裝為對(duì)象。本文將詳解BOM,DOM和事件的區(qū)別于用法,需要的可以參考一下
    2022-06-06
  • JavaScript基于ChatGPT實(shí)現(xiàn)打字機(jī)消息回復(fù)

    JavaScript基于ChatGPT實(shí)現(xiàn)打字機(jī)消息回復(fù)

    ChatGPT 是一個(gè)基于深度學(xué)習(xí)的大型語(yǔ)言模型,處理自然語(yǔ)言需要大量的計(jì)算資源和時(shí)間,響應(yīng)速度肯定比普通的讀數(shù)據(jù)庫(kù)要慢的多,本文介紹了ChatGPT打字機(jī)消息回復(fù)實(shí)現(xiàn)原理,感興趣的同學(xué)可以跟著小編一起學(xué)習(xí)
    2023-05-05
  • Js如何使用ffmpeg進(jìn)行視頻剪輯和畫(huà)面截取等功能

    Js如何使用ffmpeg進(jìn)行視頻剪輯和畫(huà)面截取等功能

    在日常處理視頻文件時(shí)常常會(huì)用到視頻片段的截取功能,FFmpeg支持該功能,下面這篇文章主要給大家介紹了關(guān)于Js如何使用ffmpeg進(jìn)行視頻剪輯和畫(huà)面截取等功能的相關(guān)資料,需要的朋友可以參考下
    2024-04-04
  • 用javascript刪除當(dāng)前行,添加行(示例代碼)

    用javascript刪除當(dāng)前行,添加行(示例代碼)

    這篇文章主要介紹了用javascript刪除當(dāng)前行,添加行的示例代碼。需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助
    2013-11-11
  • JavaScript事件學(xué)習(xí)小結(jié)(五)js中事件類型之鼠標(biāo)事件

    JavaScript事件學(xué)習(xí)小結(jié)(五)js中事件類型之鼠標(biāo)事件

    這篇文章主要介紹了JavaScript事件學(xué)習(xí)小結(jié)(五)js中事件類型之鼠標(biāo)事件的相關(guān)資料,非常不錯(cuò)具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-06-06
  • 親自教你TypeScript 項(xiàng)目搭建過(guò)程

    親自教你TypeScript 項(xiàng)目搭建過(guò)程

    這篇文章主要介紹了親自教你TypeScript 項(xiàng)目搭建過(guò)程,我記得前一天,我們配置過(guò)一份 webpack 配置,直接復(fù)制過(guò)來(lái)使用,這里就不多說(shuō)了,然后就是在項(xiàng)目中引入我們的 less,需要的朋友可以參考下
    2022-11-11
  • JavaScript編程的10個(gè)實(shí)用小技巧

    JavaScript編程的10個(gè)實(shí)用小技巧

    盡管我使用Javascript來(lái)做開(kāi)發(fā)有很多年了,但它常有一些讓我很驚訝的小特性。對(duì)于我來(lái)說(shuō),Javascript是需要持續(xù)不斷的學(xué)習(xí)的。
    2014-04-04
  • json原理分析及實(shí)例介紹

    json原理分析及實(shí)例介紹

    這次在項(xiàng)目中前后臺(tái)的數(shù)據(jù)交互中用到了json,經(jīng)過(guò)這段時(shí)間的使用,簡(jiǎn)單總結(jié)一下json的原理與使用,需要了解的朋友可以參考下
    2012-11-11

最新評(píng)論