How to addEventListener to non dom elements in Javascript?

at it's simplest the mechanism is something like:

function PubSub() {
    this.subs = {};
    this.subscribe = function(channel, sub) {
        this.subs[channel] = this.subs[channel] || []; //create array for channel
        this.subs[channel].push(sub);
    };
    this.publish = function(channel) {
        var args = [].slice.call(arguments, 1); //pop off channel argument
        this.subs[channel].forEach(function(sub) {
            sub.apply(void 0, args); //call each method listening on the channel
        });
    };
}

demo here: http://jsfiddle.net/3PNtR/

-- EDIT (~5 years later) --

same simple mechanism, newer syntax

class PubSub {
    constructor () {
        this.subs = {}
    }
    subscribe (channel, sub) {
        this.subs[channel] = this.subs[channel] || []
        this.subs[channel].push(sub)
    }
    publish (channel, ...args) {
        ;(this.subs[channel] || []).forEach(sub => sub(...args))
    }
}

There are at least two ways to achieve this:

-- If you are a JQuery user (currently, I'm not), you may wrap your simple JavaScript object with JQuery, then like DOM elements, it can listen to events. This method already has an answer here. I'm not sure how this method works under the hood, I plan to investigate later, and you may investigate by yourself.

-- With VanillaJS, you can achieve the same effect by creating an eventful 'class' from which all objects that are willing to interact will be derived/created. Then all instances of that class will be able to register, emit and broadcast an event. Borrowing semantics from the DOM API and AngularJS, I have written an example that demonstrate how this can be done and how it can be used. Here it is:

/**
 * EventfulObject constructor/base.
 * @type EventfulObject_L7.EventfulObjectConstructor|Function
 */
var EventfulObject = function() {
  /**
   * Map from event name to a list of subscribers.
   * @type Object
   */
  var event = {};
  /**
   * List of all instances of the EventfulObject type.
   * @type Array
   */
  var instances = [];
  /**
   * @returns {EventfulObject_L1.EventfulObjectConstructor} An `EventfulObject`.
   */
  var EventfulObjectConstructor = function() {
    instances.push(this);
  };
  EventfulObjectConstructor.prototype = {
    /**
     * Broadcasts an event of the given name.
     * All instances that wish to receive a broadcast must implement the `receiveBroadcast` method, the event that is being broadcast will be passed to the implementation.
     * @param {String} name Event name.
     * @returns {undefined}
     */
    broadcast: function(name) {
      instances.forEach(function(instance) {
        (instance.hasOwnProperty("receiveBroadcast") && typeof instance["receiveBroadcast"] === "function") &&
        instance["receiveBroadcast"](name);
      });
    },
    /**
     * Emits an event of the given name only to instances that are subscribed to it.
     * @param {String} name Event name.
     * @returns {undefined}
     */
    emit: function(name) {
      event.hasOwnProperty(name) && event[name].forEach(function(subscription) {
        subscription.process.call(subscription.context);
      });
    },
    /**
     * Registers the given action as a listener to the named event.
     * This method will first create an event identified by the given name if one does not exist already.
     * @param {String} name Event name.
     * @param {Function} action Listener.
     * @returns {Function} A deregistration function for this listener.
     */
    on: function(name, action) {
      event.hasOwnProperty(name) || (event[name] = []);
      event[name].push({
        context: this,
        process: action
      });

      var subscriptionIndex = event[name].length - 1;

      return function() {
        event[name].splice(subscriptionIndex, 1);
      };
    }
  };

  return EventfulObjectConstructor;
}();

var Model = function(id) {
  EventfulObject.call(this);
  this.id = id;
  this.receiveBroadcast = function(name) {
    console.log("I smell another " + name + "; and I'm model " + this.id);
  };
};
Model.prototype = Object.create(EventfulObject.prototype);
Model.prototype.constructor = Model;

// ---------- TEST AND USAGE (hopefully it's clear enough...)
// ---------- note: I'm not testing event deregistration.

var ob1 = new EventfulObject();
ob1.on("crap", function() {
  console.log("Speaking about craps on a broadcast? - Count me out!");
});

var model1 = new Model(1);

var model2 = new Model(2);
model2.on("bust", function() {
  console.log("I'm model2 and I'm busting!");
});

var ob2 = new EventfulObject();
ob2.on("bust", function() {
  console.log("I'm ob2 - busted!!!");
});
ob2.receiveBroadcast = function() {
  console.log("If it zips, I'll catch it. - That's me ob2.");
};

console.log("start:BROADCAST\n---------------");
model1.broadcast("crap");
console.log("end  :BROADCAST\n---------------\n-\n-\n");
console.log("start:EMIT\n---------------");
ob1.emit("bust");
console.log("end:EMIT\n---------------");
<h1>THE CODE IS IN the JavaScript pane!</h1>
<h3>AND... THE SHOW IS ON YOUR CONSOLE!</h3>

You may use that as a base for a more robust solution.