Подтвердить что ты не робот

Как реализовать шаблон наблюдателя в javascript?

Привет, я использую tyring для реализации шаблона наблюдателя в 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;
};

Любая помощь, которую вы можете предоставить, будет мне очень благодарна.

4b9b3361

Ответ 1

В 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 к вашей функции

Ответ 2

JavasScript управляется событиями: это означает, что он знает о времени и ожидает, что со временем все изменится. Оригинальный шаблон наблюдателя был создан для таких языков, как С++, которые не знают о времени. Вы можете использовать сильные стороны JavaScript, используя игровой цикл, чтобы проверить изменения состояния.

Создайте два элемента DOM, вход и выход

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

Настройте цикл requestAnimationFrame и начните наблюдение.

//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 framework для проверки изменений состояния в 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

Здесь реализована реализация шаблона Observer в JavaScript, которая предоставляет API, очень похожий на Backbone Models. Эта реализация позволяет избежать использования "this" и "new", как предложенной Дугласом Крокфордом.

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

Вот пример кода, который использует 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"

Ответ 4

Для меня это лучший способ реализации шаблона Observer в 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();
}

Ответ 5

Ниже представлена ​​реализация, которую я немного адаптировал к книге "Изучение шаблонов дизайна 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

Ура!

Ответ 6

Шаблон наблюдателя - все об обновлении объекта, и эти обновления автоматически отправляют событие, которое дает информацию о том, что было обновлено.

Например:

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

Ответ 7

Это старый, но я хотел дать ответ на первоначальный вопрос, "как реализовать шаблон наблюдателя с учетом существующего кода".

Шаблон Observer можно упростить как схему связи, в которой цель (наблюдаемая вещь) имеет указатель на наблюдателя (ей) и предполагает наличие открытого API для наблюдателя. Например, цель предполагает, что у наблюдателя есть метод под названием update или что наблюдатель является Function. Это то, как цель уведомляет наблюдателей об изменениях, фактически вызывая метод объекта-наблюдателя (или Function, если наблюдатель является функцией).

Каждый раз, когда свойство видоизменяется или изменяется, цель должна update всех наблюдателей, которые зарегистрировались, получить уведомление.

Здесь я предполагаю, что вы хотите знать, как реализовать Observer Key-Value. В этом случае код должен будет перебирать свой список наблюдателей и вызывать метод каждого наблюдателя update (или просто выполнять наблюдателя в случае, когда он Function), когда свойство изменяется.

var observers = null;

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

Movie.prototype.changed = function(key, old, value){
  // Assumption here is that observers can observe individual properties.
  if(!this.observers[key]) return
  
  this.observers[key].forEach( o => {
    // Assumption is that observers have an update method. This is the only
    // thing the target knows about an observer.
      o.update(key, old, value, this)
  })

}

// Now every setter on the target has to notify the observers by calling 'changed'
Movie.prototype.setTitle = function (newTitle) {
  var old = this.title;
  this.title = newTitle;
  this.changed("title", old, this.title, this)
}

Ответ 8

class EventObserver {
  constructor () {
    this.observers = []
  }

  subscribe (fn) {
    this.observers.push(fn)
  }

  unsubscribe (fn) {
    this.observers = this.observers.filter(subscriber => subscriber !== fn)
  }

  broadcast (data) {
    this.observers.forEach(subscriber => subscriber(data))
  }
}

Или вы можете использовать EventEmitter в NodeJs https://nodejs.org/api/events.html#events_class_eventemitter