2012-09-06 9 views
13

Ciao Sto tyring di implementare pattern Observer in JavaScript:come implementare pattern osservatore in javascript?

miei 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]); 
}); 

mio film:

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); 
}; 

Infine osservatore:

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; 
}; 

Qualsiasi aiuto tu possa fornire mi sarà molto grato.

risposta

12

In JavaScript, non è necessario implementare un modello di osservatore puro come in Java, perché JavaScript ha questa piccola cosa chiamata programmazione funzionale. Quindi basta usare qualcosa come http://api.jquery.com/category/callbacks-object/ invece di ObserverList.

Se si desidera utilizzare ancora l'oggetto, tutto dipende da cosa si desidera passare a ObserverList.Add. Se si tratta di un oggetto, allora avete bisogno di scrivere

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

Se si tratta di una funzione, allora è necessario scrivere

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

Inoltre è possibile utilizzare Function.apply() o Function.call() per fornire this alla funzione

+0

Vado a controllare fuori, ma se Volevo farlo funzionare così com'è, cosa devo fare? –

+1

Aggiornamento della risposta –

+0

HI Grazie Dmitry, dove dovrei aggiungere? e quale dovrei usare? quello nella classe dell'osservatore o della classe del cinema? grazie mille –

28

JavasScript è basato su eventi: Ciò significa che è a conoscenza del tempo e si aspetta che le cose cambino nel tempo. Il modello originale di Observer è stato creato per linguaggi come il C++ che non conoscono il tempo. Puoi sfruttare i punti di forza di JavaScript utilizzando un ciclo di gioco per verificare le modifiche di stato.

Creare due elementi DOM, un ingresso e un'uscita

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

impostare un loop requestAnimationFrame e iniziare osservando.

//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(); 

Questo è ciò che i motori di gioco fanno per modo immediato rendering. È anche ciò che fa il framework React per verificare i cambiamenti di stato nel DOM.

(se ne avete bisogno, ecco un semplice 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

Ecco un'implementazione del pattern Observer in JavaScript che fornisce un API molto simile a Backbone Models. Questa implementazione evita l'uso di "this" e "new", come 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; 
     }); 
     } 
    } 
    }; 
} 

Ecco qualche esempio di codice che utilizza Modello:

// 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

Questo è il modello letterale dell'oggetto. Per il modello del modulo rivelatore, entrambe le funzioni vengono ottenute, il set viene definito nella chiusura e si restituisce un oggetto alla fine del modulo 'return {get: get, set: set};' –

+0

La tua soluzione è più un Publish -scrive pattern https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern –

1

Per me questo è il modo migliore per attuare un pattern Observer in 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

cosa succede se più di un ascoltatore con la stessa funzionalità viene allegato? diciamo che methodA e methodB sottoscrivono entrambe la stessa funzione. – Dementic

0

Di seguito è un'implementazione Ho adattato un poco dal libro Learning Javascript Design Patterns.

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 

Cheers!

0

Il pattern di osservatore consiste nell'aggiornamento di un oggetto e nel momento in cui tali aggiornamenti inviano automaticamente un evento che fornisce informazioni su ciò che è stato aggiornato.

Ad esempio:

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) 
} 

Ecco un modulo del pattern Observer in javascript, è possibile dare un'occhiata al codice sorgente ribalta maggiori informazioni: https://github.com/Tixit/observe

Problemi correlati