how to implement observer pattern in javascript?

前端 未结 9 1549
执笔经年
执笔经年 2021-01-30 07:29

Hi I\'m tyring to implement observer pattern in JavaScript:

My index.js:

$(document).ready(function () {
  var ironMan = new Movie();
           


        
相关标签:
9条回答
  • 2021-01-30 08:04

    Below is an implementation i adapted a little from the book 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 讨论(0)
  • 2021-01-30 08:12

    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

    0 讨论(0)
  • 2021-01-30 08:13

    For me this is the best way to implement an Observer pattern 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 讨论(0)
  • 2021-01-30 08:15

    class Observable {
        constructor() {
           this.observer = []; 
        }
        subscribe(item) {
            this.observer.push(item);
        }
        unsubscribe(item) {
            if(!this.observer) return 'empty';
            else {
                this.observer.filter(subscribe => subscribe !== item);
            }
        }
        notify(data) {
            this.observer.forEach(item => item(data));
        }
    }
    
    var p1 = document.querySelector('.p1');
    var p2 = document.querySelector('.p2');
    var p3 = document.querySelector('.p3');
    var input = document.querySelector('.input');
    
    const update1 = text => p1.textContent = text;
    const update2 = text => p2.textContent = text;
    const update3 = text => p3.textContent = text;
    
    var observarble = new Observable();
    observarble.subscribe(update1);
    observarble.subscribe(update2);
    observarble.subscribe(update3);
    
    input.addEventListener('keyup', event => observarble.notify(event.target.value));
    <input type="input" class="input" />
    <div class="p1"></div>
    <div class="p2"></div>
    <div class="p3"></div>

    Observer is one of the popular patterns that use across all javascript applications.

    The instance (subject) maintains a collection of objects (observers) and notifies them all when changes to the state occur.

    Let's explain by writing some logic

    class Observable {
        constructor() {
           this.observer = []; 
        }
        subscribe(item) {
            this.observer.push(item);
        }
        unsubscribe(item) {
            if(!this.observer) return 'empty';
            else {
                this.observer.filter(subscribe => subscribe !== item);
            }
        }
        notify(data) {
            this.observer.forEach(item => item(data));
        }
    }
    

    Now your question will be what's next ??

    Where to actually use this pattern.

    Imagine that you have to update multiple elements simultaneously when some event occurs.

    Add some HTML in your code

    <input type="input" class="input" />
    <div class="p1"></div>
    <div class="p2"></div>
    <div class="p3"></div>
    

    Get those nodes using Javascript

    var p1 = document.querySelector('.p1');
    var p2 = document.querySelector('.p2');
    var p3 = document.querySelector('.p3');
    var input = document.querySelector('.input');
    

    To set the value using observer you need to add their text content

    const update1 = text => p1.textContent = text;
    const update2 = text => p2.textContent = text;
    const update3 = text => p3.textContent = text;
    
    
    var observarble = new Observable();
    observarble.subscribe(update1);
    observarble.subscribe(update2);
    observarble.subscribe(update3);
    

    One last thing attach an event listener with input keyup/change

    input.addEventListener('keyup', ev => observarble.notify(ev.target.value));
    

    That's it :) !!

    Link for working demo https://codepen.io/nishant5857/pen/MWKdByY

    0 讨论(0)
  • 2021-01-30 08:16

    JavasScript 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);
              };
    })();
    
    0 讨论(0)
  • 2021-01-30 08:20
    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))
      }
    }
    

    Or you can use EventEmitter in NodeJs https://nodejs.org/api/events.html#events_class_eventemitter

    0 讨论(0)
提交回复
热议问题