<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                觀察者模式和發布-訂閱模式有區別嗎? 在深入探討區別之前,我們先來分別討論下“觀察者模式”和“發布-訂閱模式” # 觀察者模式 我認為大多數人都會同意觀察者模式是學起來最好入門的,因為你從字面意思就能知道它主要是做什么的。 > 觀察者模式在軟件設計中是一個對象,維護一個依賴列表,當任何狀態發生改變自動通知它們。 看吧,即使是維基百科的定義,也不是很難, 對吧? 如果你還不清楚,那讓我用通俗易懂的來解釋。 我們假設你正在找一份軟件工程師的工作,對“香蕉公司”很感興趣。所以你聯系了他們的HR,給了他你的聯系電話。他保證如果有任何職位空缺都會通知你。這里還有幾個候選人也你一樣很感興趣。所以職位空缺大家都會知道,如果你回應了他們的通知,他們就會聯系你面試。 所以,以上和“觀察者模式”有什么關系呢?這里的“香蕉公司”就是Subject,用來維護Observers(和你一樣的候選人),為某些event(比如職位空缺)來通知(notify)觀察者。 是不是很簡單!? ![](https://box.kancloud.cn/ccf0461227a80d6c5b02f85762b33dae_1339x497.png) ``` class Subject { constructor() { this.subs = [] } addSub(sub) { this.subs.push(sub) } notify() { this.subs.forEach(sub => { sub.update() }) } } class Observer { update() { console.log('update') } } let subject = new Subject() let observer = new Observer() subject.addSub(observer) subject.notify() ``` # 發布-訂閱模式 在觀察者模式中的Subject就像一個發布者(Publisher),而觀察者(Observer)完全可以看作一個訂閱者(Subscriber)。subject通知觀察者時,就像一個發布者通知他的訂閱者。這也就是為什么很多書和文章使用“發布-訂閱”概念來解釋觀察者設計模式。但是這里還有另外一個流行的模式叫做發布-訂閱設計模式。它的概念和觀察者模式非常類似。最大的區別是: > 在發布-訂閱模式中,消息的發送方,叫做發布者(publishers),消息不會直接發送給特定的接收者(訂閱者)。 意思就是發布者和訂閱者不知道對方的存在。需要一個第三方組件,叫做信息中介,它將訂閱者和發布者串聯起來,它過濾和分配所有輸入的消息。換句話說,發布-訂閱模式用來處理不同系統組件的信息交流,即使這些組件不知道對方的存在。 那么如何過濾消息的呢?事實上這里有幾個過程,最流行的方法是:基于主題以及基于內容。好了,就此打住,如果你感興趣,可以去維基百科了解。 ![](https://box.kancloud.cn/5554cb71a2f07989b5b279d29e5fe2c2_450x266.png) ``` let watcher = (() => { let handlers = {} function sub(name, callback) { if (!handlers[name]) { handlers[name] = [] } handlers[name].push(callback) } function pub(name, ...args) { if (!handlers[name]) { return } handlers[name].forEach(fn => { fn(...args) }) } return { sub, pub } })() watcher.sub('test', (a, b) => { console.log(a, b) }) watcher.sub('test', a => { console.log(a) }) watcher.pub('test', 1, 2) ``` # 區別 我用下圖表示這兩個模式最重要的區別: ![](https://box.kancloud.cn/67ae957155de3afb10852e350fce5255_512x406.png) 我們把這些差異快速總結一下: * 在觀察者模式中,觀察者是知道Subject的,Subject一直保持對觀察者進行記錄。然而,在發布訂閱模式中,發布者和訂閱者不知道對方的存在。它們只有通過消息代理進行通信。 * 在發布訂閱模式中,組件是松散耦合的,正好和觀察者模式相反。 * 觀察者模式大多數時候是同步的,比如當事件觸發,Subject就會去調用觀察者的方法。而發布-訂閱模式大多數時候是異步的(使用消息隊列)。 * 觀察者模式需要在單個應用程序地址空間中實現,而發布-訂閱更像交叉應用模式。 盡管它們之間有區別,但有些人可能會說發布-訂閱模式是觀察者模式的變異,因為它們概念上是相似的。 # 測試題 請實現下面的自定義事件 Event 對象的接口,功能見注釋(測試1) 該 Event 對象的接口需要能被其他對象拓展復用(測試2) ``` // 測試1 Event.on('test', function (result) { console.log(result); }); Event.on('test', function () { console.log('test'); }); Event.emit('test', 'hello world'); // 輸出 'hello world' 和 'test' // 測試2 var person1 = {}; var person2 = {}; Object.assign(person1, Event); Object.assign(person2, Event); person1.on('call1', function () { console.log('person1'); }); person2.on('call2', function () { console.log('person2'); }); person1.emit('call1'); // 輸出 'person1' person1.emit('call2'); // 沒有輸出 person2.emit('call1'); // 沒有輸出 person2.emit('call2'); // 輸出 'person2' var Event = { // 通過on接口監聽事件eventName // 如果事件eventName被觸發,則執行callback回調函數 on: function (eventName, callback) { //你的代碼 }, // 觸發事件 eventName emit: function (eventName) { //你的代碼 } }; ``` 先來做測試1,通過觀察,這就是上面所講的發布-訂閱模式,于是DuangDuangDuang的敲代碼吧 ``` let Event = (() => { let handlers = {} function on(eventName, callback) { if (!handlers[eventName]) { handlers[eventName] = [] } handlers[eventName].push(callback) } function emit(eventName, ...args) { if (handlers[eventName] && handlers[eventName].length > 0) { handlers[eventName].forEach(callback => { callback(...args) }) } } return { on, emit } })() ``` 完美通過測試1,接下來看測試2 測試2中有這樣一段代碼:`Object.assign()`,我們先來看看這是干嘛的。 > `Object.assign(target, ...sources)`這個是ES6的新對象方法,用于對象的合并,將源對象(source)的所有**可枚舉**屬性,復制到目標對象(target) Object.assign()接口可以接收多個參數,第一個參數是目標對象,后面的都是源對象,assign方法將多個原對象的屬性和方法都合并到了目標對象上面,如果在這個過程中出現同名的屬性(方法),后合并的屬性(方法)會覆蓋之前的同名屬性(方法)。 Object.assign()是淺拷貝,對于引用類型的數據,拷貝的是其引用,不會具體的值,舉個例子: ``` let a = { b: { c: 3 } } let d = Object.assign({}, a) d.b.c = 5 console.log(a.b.c) // 5 ``` 但是,需要注意的一點是,對象第一層的引用類型屬性是深拷貝 ``` let d = Object.assign({}, a) d.b = { e: 6 } console.log(a.b) // {c: 3} ``` 接下來我們直接測試上面的代碼 ``` person1.emit('call1') // person1 person1.emit('call2') // person2.emit('call1') // person2.emit('call2') // person2 ``` OK 完美通過,但是看到網上有些文章的測試結果卻是下面這樣: ``` person1.emit('call1') // person1 person1.emit('call2') // person2 person2.emit('call1') // person1 person2.emit('call2') // person2 ``` 出現這個結果的原因可能是assign方法之前版本都是淺拷貝,包括第一層屬性,而目前版本已經修改了這個問題吧!! 但是淺拷貝為什么會出現上面的結果呢?原因就是如果源對象某個屬性的值是對象,那么目標對象拷貝得到的是這個對象的引用。由于進行測試一的時候調用了on方法,所以event里面已經有了handles這個可枚舉的屬性。然后再分別合并到兩個person里面的話,兩個person對象里面的handles都只是一個引用。所以就互相影響了。 所以,終極完美解決辦法就是:摒棄淺拷貝,實現深拷貝。但是題目已經固定必須使用assign方法,那么從另一方面入手:我們將handles這個屬性定義為**不可枚舉**的,然后在person調用on方法的時候再分別產生handles這個對象。 ``` var Event = { // 通過on接口監聽事件eventName // 如果事件eventName被觸發,則執行callback回調函數 on: function (eventName, callback) { //你的代碼 if(!this.handles){ //this.handles={}; Object.defineProperty(this, "handles", { value: {}, enumerable: false, configurable: true, writable: true }) } if(!this.handles[eventName]){ this.handles[eventName]=[]; } this.handles[eventName].push(callback); }, // 觸發事件 eventName emit: function (eventName) { //你的代碼 if(this.handles[arguments[0]]){ for(var i=0;i<this.handles[arguments[0]].length;i++){ this.handles[arguments[0]][i](arguments[1]); } } } }; ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看