オブザーバー パターンは、パブリッシュ/サブスクライブ パターンとも呼ばれ、最も一般的に使用されるデザイン パターンの 1 つです。次の記事では主に、JavaScript のオブザーバー パターンの関連資料を詳しく紹介します。
はじめに
オブザーバー パターンは、パブリッシュ/サブスクライブ パターン (パブリッシュ/サブスクライブ) とも呼ばれ、複数のオブザーバー オブジェクトが特定のトピック オブジェクトを同時に監視できるようにします。このトピック オブジェクトのステータス 変更が発生するとすべてのオブザーバー オブジェクトに通知が送信され、オブザーバー オブジェクト自体が自動的に更新されます。正直に言うと、少し低レベルのコードを書かない限り、それを使用しないかもしれません。 しかし、これを使用すると、コードがより柔軟かつ規則的になり、冗長なコードが減り、モジュールや関数の開発が容易になります。
オブザーバー パターンを使用する利点:
は、単純なブロードキャスト通信をサポートし、サブスクライブされたすべてのオブジェクトに自動的に通知します。
ページが読み込まれた後、ターゲット オブジェクトはオブザーバーとの動的な関係を簡単に持つことができ、柔軟性が向上します。
ターゲットオブジェクトとオブザーバーの間の抽象的な結合関係は、独立して拡張して再利用できます。
はじめに
フロントエンド ビジネスで最も一般的に使用される場所はカスタム イベントかもしれません。
実際、ブラウザのイベントもオブザーバーモードです
p.onclick = function click() { console.log('click') }
ここで、関数 click は p の click イベントをサブスクライブし、マウスがクリックされると、イベントが発行され、対応する関数が実行されます。この関数 click はオブザーバーです。
具体的な理解
実際、コードの実装を見るだけで理解できます。しかし、これらのプログラミングモデルの設計は人生経験からもたらされます。したがって、具体的な理解も非常に重要な経験です。
結婚披露宴を例に挙げてみましょう。たとえば、あなたの親友が結婚するということは、毎日起こることではなく、一生に一度か二度しか起こりません(おそらくそれ以上)。つまり、私たちが「彼の結婚式に行く」ということは絶対に起こりません。毎日、しかし特定のときだけ。絶対に毎日彼に「今日は結婚するの?宴会に来るの?」なんて聞くことはできません。 1 回か 2 回は問題ありませんが、毎日尋ねてください。あなたがパートナーを見つけられない独身男性で、あなたが毎日彼にこれを尋ねたとしても、彼はあなたを殺しません。 。
次に、ここで「通知する」イベントをリリースする必要があります。
私はオブザーバーとして、彼の「結婚」のイベントに参加しました。私たちは良い友人であり、必ず彼の結婚式に出席することに同意しました。その場合、私は観察者となり、「結婚式に行く」が対応する行動となります。 「結婚」イベントに登録すると、女の子をナンパする、ディナーデートをする、映画を見る、デートするなど、何をすべきか、毎日彼に尋ねる必要はありません。
彼が「結婚」イベントを投稿すると、通知が私に送信され、私は特定の時間に「結婚披露宴に出席する」アクション機能を実行しに行きました...
//模拟代码 //我订阅了'marry' 事件 wo.on('marry',function(){ //去参加婚礼酒席 }) //然后他发布。比如浏览器的点击 // 对应的我的 function就会执行
デカップリング/モジュール/function
実際、コードでは、公開とサブスクリプションを管理するために中間サービスに似た仲介者が必要です。
たとえば、ブラウザーのイベント ハンドラーはサブスクリプション インターフェイスを提供し、「イベント」シグナルを受信してそれを公開します。 js コードにブラウザーとの接続と対話を持たせます。しかし、元々はこの 2 つは別のものでした。
私の意見では、オブザーバー パターンの最大の利点は分離です。これにより、コードを関数とモジュールに分離できるようになり、コードがより明確になり、開発コストが削減され、保守が容易になります。
例:
1. プロジェクトのビュー表示レイヤーとモデル (データ処理) ロジックレイヤーの場合、最初にページ、Ajax、文字列スプライシングを作成し、まとめるインターフェイスをリクエストしてから、それを渡します。ドム。おそらく、js ファイルと、インターフェイスを要求し、ビューの表示を担当する関数があると思います。
var xhr = new XMLHttpRequest () xhr.open('get',url) xhr.onreadystatechange = function () { if(this.readyState !== 4) return if(this.status === 200) { ps.innerHTML = '<p>' + this.response + '</p>' // } } xhr.responseType = 'json' xhr.send(null)
実際、リクエストは表示レンダリングから分離する必要があります。
//请求 function getData () { var xhr = new XMLHttpRequest () xhr.open('get',url) xhr.onreadystatechange = function () { if(this.readyState !== 4) return if(this.status === 200) { this.emit('渲染') // 发布 } } xhr.responseType = 'json' xhr.send(null) } //渲染 function view () {} xhr.on('渲染',view)
ステータス コード 200 に直接コールバックを配置することによってもこれを行うことができます。しかし、異なる処理を行う 2 つのレンダリング関数がある場合、毎回異なる関数に変更する必要があるのでしょうか? 同じリクエスト処理を再度書かなければなりませんか?
観察者の言葉
function view1 () {} function view2 () {} function view3 () {} function view4 () {} if(我要渲染view1) { xhr.on('渲染',view1) //订阅 xhr.on('渲染',view2) }else{ xhr.on('渲染',view3) xhr.on('渲染',view4) }
利点は、getData 関数がデータのリクエストのみを担当し、メソッドを追加するためのインターフェイスを公開してくれることです。このように、私の getData は比較的完全な機能モジュールであり、状況がいくつであっても、getData 内のコードは変わりません。
業務を実装したり、新しい機能を追加したりするために、以前に書いたコードを変更することがあり、元の機能モジュールが認識できないほど変更されてしまうことがあります。
そして、重複したコードがたくさんあります。
プロセス?それともモジュール?
もちろん、優れた完全な機能モジュールを封印することは非常に困難ですが、少なくとも開始は必要です。
メソッドを追加するためにサブスクライブし、イベント プールの公開時にメソッドを実行します。
2. MV*クラスのフレームワーク
MVC也是一种设计模式,这里面也都应用了观察者。
他内部也都是各种发布订阅,好像是一个观察者模型,从而实现了一个模拟的内存中的dom改变,计算出那个DOM节点应该改变。当然具体实现要做好多事情…就不…
3、redux
简单实现一个createstore函数
//这是一个工厂函数,可以创建store const createStore = (reducer) => { let state; // 定义存储的state let listeners = []; // getState的作用很简单就是返回当前是state const getState = ()=> state; //定义一个派发函数 //当在外界调用此函数的时候,会修改状态 const dispatch = (action)=>{ //调用reducer函数修改状态,返回一新的状态并赋值给这个局部状态变量 state = reducer(state,action); //依次调用监听函数,通知所有的监听函数 listeners.forEach(listener => listener()); } //订阅此状态的函数,当状态发生变化的时候记得调用此监听函数 const subscribe = function(listener){ //先把此监听 加到数组中 listeners.push(listener); //返回一个函数,当调用它的时候将此监听函数从监听数组移除 return function(){ listeners = listeners.filter(l => l != listener); } } //默认调用一次dispatch给state赋一个初始值 dispatch(); return { getState, dispatch, subscribe } } let store = createStore(reducer); //把数据渲染到界面上 const render = () => { document.body.innerText = store.getState(); } // 订阅状态变化事件,当状态变化时用监听函数 store.subscribe(render); render(); var INCREASE_ACTION = {type: 'INCREMENT'}; document.addEventListener('click', function (e) { //触发一个Action store.dispatch(INCREASE_ACTION); })
4、在node 中的作用 大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。
实现一个可以发布订阅的类
'use strict' class EmitterEvent { constructor() { //构造器。实例上创建一个事件池 this._event = {} } //on 订阅 on (eventName, handler) { // 根据eventName,事件池有对应的事件数组, 就push添加,没有就新建一个。 // 严谨一点应该判断handler的类型,是不是function if(this._event[eventName]) { this._event[eventName].push(handler) } else { this._event[eventName] = [handler] } } emit (eventName) { // 根据eventName找到对应数组 var events = this._event[eventName]; // 取一下传进来的参数,方便给执行的函数 var otherArgs = Array.prototype.slice.call(arguments,1) var that = this if(events) { events.forEach((event) => { event.apply(that, otherArgs) }) } } // 解除订阅 off (eventName, handler) { var events = this._event[eventName] if(events) { this._event[eventName] = events.filter((event) => { return event !== handler }) } } // 订阅以后,emit 发布执行一次后自动解除订阅 once (eventName, handler) { var that = this function func () { var args = Array.prototype.slice.call(arguments,0) handler.apply(that, args) this.off(eventName,func) } this.on(eventName, func) } } var event = new EmitterEvent() function a (something) { console.log(something,'aa-aa') } function b (something) { console.log(something) } event.once('dosomething',a) event.emit('dosomething', 'chifan') //event.emit('dosomething') // event.on('dosomething',a) // event.on('dosomething',b) // event.emit('dosomething','chifan') // event.off('dosomething',a) // setTimeout(() => { // event.emit('dosomething','hejiu') // },2000)
上面是我整理给大家的,希望今后会对大家有帮助。
相关文章:
以上がJavascript のオブザーバー パターンの詳細な説明 (グラフィック チュートリアル)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。