how to implement observer pattern in javascript?

JavaScript is event-driven: That means it's aware of time and expects things to change over time. The original Observer Pattern was created for languages like C++ that aren't aware of time. You can leverage JavaScript's strengths by using a game loop to check for state changes.

Create two DOM elements, an input and output

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

Set up a requestAnimationFrame loop and start observing.

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

This is what game engines do for immediate mode rendering. It's also what the React framework does to check for state changes in the DOM.

(If you need it, here's a simple 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);
          };
})();

In JavaScript, there is no point to implement pure observer pattern as in Java, because JavaScript has this little thing called functional programming. So just use something like http://api.jquery.com/category/callbacks-object/ instead of your ObserverList.

If you still want to use your object, then everything depends on what do you want to pass to ObserverList.Add. If it is some object, then you need to write

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

If it is a function then you need to write

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

Also you can use Function.apply() or Function.call() to supply this to your function


Here's an implementation of the Observer pattern in JavaScript that provides an API very similar to Backbone Models. This implementation avoids use of "this" and "new", as 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;
        });
      }
    }
  };
}

Here's some example code that uses Model:

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