2012-09-06 23 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