2012-09-06 59 views
13

嗨,我是tyring實現在JavaScript觀察者模式:如何在javascript中實現觀察者模式?

我index.js

$(document).ready(function() { 
    var ironMan = new Movie(); 
    ironMan.setTitle('IronMan'); 
    ironMan.setRating('R'); 
    ironMan.setId(1); 
    // ironMan.setCast(['Robert Downey Jr.', 'Jeff Bridges', 'Gwyneth Paltrow']); 

    var terminator = new Movie(); 
    terminator.setTitle('Terminator'); 
    terminator.setRating('P'); 
    terminator.setId(2); 

    console.log(ironMan.toString()); 
    console.log(terminator.toString()); 

    ironMan.play(); 
    ironMan.stop(); 
    ironMan.download(); 
    ironMan.share('V. Rivas'); 

    console.log(ironMan.getCast()[0]); 
}); 

我的電影

var title; 
var rating; 
var id; 
var observers; 


function Movie() { 
    observers = new ObserverList(); 
} 

//function Movie (title, rating, id){ 
// this. title = title; 
// this.rating = rating; 
// this.id =id; 
// observers = new ObserverList(); 
//} 

Movie.prototype.setTitle = function (newTitle) { 
    this.title = newTitle; 
} 

Movie.prototype.getTilte = function() { 
    return this.title; 
} 

Movie.prototype.setRating = function (newRating) { 
    this.rating = newRating; 
} 

Movie.prototype.getRating = function() { 
    return this.rating; 
} 

Movie.prototype.setId = function (newId) { 
    this.id = newId; 
} 

Movie.prototype.getId = function() { 
    return this.id; 
} 

Movie.prototype.play = function() { 
    for (i = 0; i < observers.Count; i++) { 
    console.log("palying..."); 
    } 
} 

Movie.prototype.stop = function() { 
    for (i = 0; i < observers.Count; i++) { 
    console.log("stoped"); 
    } 
} 

Movie.prototype.AddObserver = function (observer) { 
    observers.Add(observer); 
}; 

最後觀察者

function ObserverList() { 
    this.observerList = []; 
} 

ObserverList.prototype.Add = function (obj) { 
    return this.observerList.push(obj); 
}; 

ObserverList.prototype.Empty = function() { 
    this.observerList = []; 
}; 

ObserverList.prototype.Count = function() { 
    return this.observerList.length; 
}; 

ObserverList.prototype.Get = function (index) { 
    if (index > -1 && index < this.observerList.length) { 
    return this.observerList[index]; 
    } 
}; 

ObserverList.prototype.Insert = function (obj, index) { 
    var pointer = -1; 

    if (index === 0) { 
    this.observerList.unshift(obj); 
    pointer = index; 
    } else if (index === this.observerList.length) { 
    this.observerList.push(obj); 
    pointer = index; 
    } 

    return pointer; 
}; 

你可以提供的任何幫助,我將非常感激。

回答

12

在JavaScript中,沒有必要像在Java中一樣實現純觀察者模式,因爲JavaScript有這個小功能編程。所以只需使用http://api.jquery.com/category/callbacks-object/而不是ObserverList。

如果你仍然想使用你的對象,那麼一切都取決於你想傳遞給ObserverList.Add。如果是某個對象,那麼你需要寫

for(i = 0; i < observers.Count; i++) { 
    observers[i].Notify("some data"); 
} 

如果它是一個功能,那麼你需要寫

for(i = 0; i < observers.Count; i++) { 
    observers[i]("Some data"); 
} 

您也可以使用Function.apply()或Function.call()提供this到您的功能

+0

我會檢查出來,但如果我想讓它保持原樣,我該怎麼辦? –

+1

更新了答案 –

+0

嗨,謝謝德米特里,我應該在哪裏添加?我應該使用哪一個?在觀察員班或電影班中的人?非常感謝你 –

28

JavasScript是事件驅動:這意味着它意識到時間,並期望事情隨時間而改變。原始觀察者模式是爲C++等語言創建的,它們不知道時間。 您可以使用遊戲循環來檢查狀態更改,從而充分利用JavaScript的優勢。

創建兩個DOM元素,輸入和輸出

<input type="text" value="Enter some text..."> 
<p id="output"> 

設置一個​​環,並開始觀察。

//Get a reference to the input and output 
var input = document.querySelector("input"); 
var output = document.querySelector("#output"); 

//Set up a requestAnimationFrame loop 
function update() { 
    requestAnimationFrame(update); 

    //Change the output to match the input 
    output.innerHTML = input.value; 
} 
update(); 

這是什麼遊戲引擎立即模式渲染做。這也是React框架檢查DOM中狀態更改的方法。

(如果你需要它,這裏有一個簡單的requestAnimationPolyfill)

//Polyfill for requestAnimationFrame 
window.requestAnimationFrame = (function(){ 
    return window.requestAnimationFrame  || 
      window.webkitRequestAnimationFrame || 
      window.mozRequestAnimationFrame || 
      window.oRequestAnimationFrame  || 
      window.msRequestAnimationFrame  || 
      function(/* function */ callback, /* DOMElement */ element){ 
      window.setTimeout(callback, 1000/60); 
      }; 
})(); 
3

這裏的JavaScript中的觀察者模式,實現了非常類似的Backbone Models API的實現。該實現避免使用「this」和「new」,如suggested by Douglas Crockford

// The constructor function. 
function Model(){ 

    // An object containing callback functions. 
    // * Keys are property names 
    // * Values are arrays of callback functions 
    var callbacks = {}, 

     // An object containing property values. 
     // * Keys are property names 
     // * Values are values set on the model 
     values = {}; 

    // Return the public Model API, 
    // using the revealing module pattern. 
    return { 

    // Gets a value from the model. 
    get: function(key){ 
     return values[key]; 
    }, 

    // Sets a value on the model and 
    // invokes callbacks added for the property, 
    // passing the new value into the callback. 
    set: function(key, value){ 
     values[key] = value; 
     if(callbacks[key]){ 
     callbacks[key].forEach(function (callback) { 
      callback(value); 
     }); 
     } 
    }, 

    // Adds a callback that will listen for changes 
    // to the specified property. 
    on: function(key, callbackToAdd){ 
     if(!callbacks[key]){ 
     callbacks[key] = []; 
     } 
     callbacks[key].push(callbackToAdd); 
    }, 

    // Removes a callback that listening for changes 
    // to the specified property. 
    off: function(key, callbackToRemove){ 
     if(callbacks[key]){ 
     callbacks[key] = callbacks[key].filter(function (callback) { 
      return callback !== callbackToRemove; 
     }); 
     } 
    } 
    }; 
} 

下面是一個使用模型的一些示例代碼:

// Create a new model. 
var model = Model(); 

// Create callbacks for X and Y properties. 
function listenX(x){ 
    // The new value is passed to the callback. 
    console.log('x changed to ' + x); 
} 

function listenY(y){ 
    // The new value can be extracted from the model. 
    console.log('y changed to ' + model.get('y')); 
} 

// Add callbacks as observers to the model. 
model.on('x', listenX); 
model.on('y', listenY); 

// Set values of X and Y. 
model.set('x', 30); // prints "x changed to 30" 
model.set('y', 40); // prints "y changed to 40" 

// Remove one listener. 
model.off('x', listenX); 
model.set('x', 360); // prints nothing 
model.set('y', 50); // prints "y changed to 40" 
+0

這是對象字面模式。對於揭示模塊模式,函數get,set將在閉包中定義,並且在窗體'return {get:get,set:set}'末尾返回一個對象;' –

+0

您的解決方案更多是發佈 - 訂閱模式https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern –

1

對我來說這是實現在JS的觀察者模式的最佳方式

function Click() { 
    this.handlers = []; // observers 
} 

Click.prototype = { 

    subscribe: function(fn) { 
     this.handlers.push(fn); 
    }, 

    unsubscribe: function(fn) { 
     this.handlers = this.handlers.filter(
      function(item) { 
       if (item !== fn) { 
        return item; 
       } 
      } 
     ); 
    }, 

    fire: function(o, thisObj) { 
     var scope = thisObj || window; 
     this.handlers.forEach(function(item) { 
      item.call(scope, o); 
     }); 
    } 
} 

// log helper 

var log = (function() { 
    var log = ""; 

    return { 
     add: function(msg) { log += msg + "\n"; }, 
     show: function() { alert(log); log = ""; } 
    } 
})(); 

function run() { 

    var clickHandler = function(item) { 
     log.add("fired: " + item); 
    }; 

    var click = new Click(); 

    click.subscribe(clickHandler); 
    click.fire('event #1'); 
    click.unsubscribe(clickHandler); 
    click.fire('event #2'); 
    click.subscribe(clickHandler); 
    click.fire('event #3'); 

    log.show(); 
} 
+0

如果多個具有相同功能的偵聽器被連接,會發生什麼情況?可以說,methodA和methodB都訂閱相同的功能。 – Dementic

0

下面是我適應一個實現學習JavaScript設計模式的書很少。

function pubsub(obj) { 

var events = {}, 
    subUid = -1; 

obj.publish = function (event, args) { 

    if (!events[event]) { 
     return false; 
    } 

    var subscribers = events[event], 
    len = subscribers ? subscribers.length : 0; 

    while (len--) { 
     subscribers[len].func(event, args); 
    } 
}; 

obj.subscribe = function (event, func) { 

    if (!events[event]) { 
     events[event] = []; 
    } 

    var token = (++subUid).toString(); 
    events[event].push({ 
     token: token, 
     func: func 
    }); 

    return token; 

}; 

obj.unsubscribe = function (token) { 

    for (var event in events) { 
     if (events.hasOwnProperty(event)) { 
      for (var i = 0, j = events[event].length ; i < j ; i++) { 
       if (events[event][i].token === token) { 
        events[event].splice(i, 1); 
       } 
      } 
     } 
    } 

    return this; 

}; 

} 

var obj = {}; // Any javascript object 
pubsub(obj); // Make an observable from the given object 

var subscription = obj.subscribe('load', handler); 

// event handler callback 
function handler(event, data) { 
    console.log(event, data); 
} 

obj.publish('load', 'Data loaded successfully'); // logs 'load Data loaded successfully' 
obj.unsubscribe(subscription); 
obj.publish('load', 'Data loaded successfully'); // nothing happens 

乾杯!

0

觀察者模式是關於更新一個對象,讓這些更新自動發出一個事件,提供有關更新內容的信息。

例如:

function ObserverList(){ 
    this.observerList = [] 
    this.listeners = [] 
} 

ObserverList.prototype.add = function(obj){ 
    this.observerList.push(obj) 
    this.listeners.forEach(function(callback){ 
    callback({type:'add', obj:obj}) 
    }) 
} 

ObserverList.prototype.onChange = function(callback){ 
    this.listeners.push(callback) 
} 

這裏的JavaScript中的觀察者模式的模塊,你可以看看源代碼脫穎而出的詳細信息:https://github.com/Tixit/observe