介紹一種觀察者模式其實觀察者模式是一個比較簡單的 Publish,Subscribe過程,整個過程概括起來說就是對 對訂閱者這個數組的操作,而消息的發佈其實就是 遍歷訂閱者對象調用訂閱者本身方法的一個過程,在實際中的應用的話,還需要用setter 方法做一下數據劫持
function Publisher(){
this.observers = [];
this.state = "";
}
Publisher.prototype.addOb=function(observer){
var flag = false;
for (var i = this.observers.length - 1; i >= 0; i--) {
if(this.observers[i]===observer){
flag=true;
}
};
if(!flag){
this.observers.push(observer);
}
return this;
}
Publisher.prototype.removeOb=function(observer){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
if(observers[i]===observer){
observers.splice(i,1);
}
};
return this;
}
Publisher.prototype.notice=function(){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
observers[i].update(this.state);
};
}
一個構造函數,外加原型上的幾個方法即可簡單解決
在對 觀察者進行遍歷的時候, 可以用過濾器等進行下攔截
訂閱者需要一個Update的方法 由於不同對象對這個update 方法行爲表現不同 , 每個對象可以有不同的實現 :
function Subscribe(){
this.update = function(data){
console.log(data);
};
}
//實際應用
var oba = new Subscribe(),
obb = new Subscribe();
var pba = new Publisher();
pba.addOb(oba);
pba.addOb(obb);
oba.update = function(state){
console.log(state+"hello!");
}
obb.update = function(state){
console.log(state+"world!");
}
pba.state = "open ";
pba.notice();
大家看到,我們在最後對發佈者手動設置了它的內容(state)並且要求他發出通知(notice)。在實際項目中,發佈者的內容可能是從後臺獲取的也可能是從前臺某地方輸入的。然而發佈者每次更新內容後又要手動調用通知是不是有點多餘呢?既然更新了內容那就肯定要通知別人了啊。那我們就把內容的更新與發出通知進行綁定好了,看下面的代碼
對於以上的內容,或許並沒有跟我們的項目中實際出現的問題有關,那我們就來代入這種設計模式,做一個例子:三個文本框ABC,其中A可編輯,B與C不可編輯且B的值是A的值加上後綴”@w3c.com”,C的值是A的值加上前綴”ID-“。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div>
<label>用戶名稱:<input type="text" id="pba" placeholder="請輸入用戶名稱" /></label><br /><br />
<label>生成郵箱:<input type="text" id="oba" readonly /></label>
<label>生成ID:<input type="text" id="obb" readonly /></label>
</div>
<script type="text/javascript">
//發佈者
function Publisher(obj){
this.observers = [];
var state = obj.value; //讓該內容不能直接訪問
//新增兩個對於state的操作 獲取/更新
this.getState=function(){
return state;
}
this.setState=function(value){
state = value;
this.notice();
}
this.obj = obj;
}
Publisher.prototype.addOb=function(observer){
var flag = false;
for (var i = this.observers.length - 1; i >= 0; i--) {
if(this.observers[i]===observer){
flag=true;
}
};
if(!flag){
this.observers.push(observer);
}
return this;
}
Publisher.prototype.removeOb=function(observer){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
if(observers[i]===observer){
observers.splice(i,1);
}
};
return this;
}
Publisher.prototype.notice=function(){
var observers = this.observers;
for (var i = 0; i < observers.length; i++) {
observers[i].update(this.getState());
};
}
//訂閱者
function Subscribe(obj){
this.obj = obj;
this.update = function(data){
this.obj.value = data;
};
}
//實際應用
var oba = new Subscribe(document.querySelector("#oba")),
obb = new Subscribe(document.querySelector("#obb"));
var pba = new Publisher(document.querySelector("#pba"));
pba.addOb(oba);
pba.addOb(obb);
oba.update = function(state){
this.obj.value = state+"@w3c.com";
}
obb.update = function(state){
this.obj.value = "ID-"+state;
}
pba.obj.addEventListener('keyup',function(){
pba.setState(this.value);
});
</script>
</body>
</html>
在《大話設計模式》一書中,提到類似的情況:如果針對發佈者內容而訂閱者要做不同的事情呢?比如一個按鈕和三個矩形,點擊按鈕的時候,第一個矩形增加寬度,第二個矩形增加高度,第三個矩形則變成圓角矩形又該怎麼做呢?當然我們可以在三個矩形的update內部寫具體的實現代碼,但是這update豈不是沒有一個具體的功能描述了嗎?該書中用“事件委託”解決了這個問題(此處事件委託和DOM中的事件委託應該是兩碼事),我個人理解這個“事件委託”在javascript中可以用一個數組表示,然後裏面放各個訂閱者的不同名字的update,然後一一調用。
在《javascript設計模式》一書中,關於觀察者模式的實現也是採用”推“這種方式,章節的最後反問到如何實現”拉“這種方式呢?
我個人理解:發佈者推送數據的時候有強制性,促使訂閱者更新(update),然而在”拉“這種模式中,發佈者本身僅僅包含最新的內容,沒有通知(notice)沒有訂閱者列表,當訂閱者需要得到數據的時候在其對應的update方法裏面傳入發佈者對象即可。小白之見,請對該模式有不同理解的道友多多指正。