9

I am trying to have my Node.js object cast events. Now this is not a problem if I make 'static' objects and instantiate them, but how do I do this when my object has no static grandpa, like when an object was created using object literal notation?

I am used to writing ExtJS syntax so I prefer everything in object literal.

// var EventEmitter = require('events').EventEmitter; // How where when?

myObject = {
  myFunction: function() {
    console.log('foo');
  },
  urFunction: function() {
    console.log('bar');
  }
};

This is an object. It does not have a constructor, because there don't need to be more instances.

Now how do I allow myObject to emit events?

I have tried and tried to adapt code, but I cannot get it to work without rewriting my object to a form with a constructor like so:

var EventEmitter = require('events').EventEmitter;
var util = require('util');

function myClass() {
  EventEmitter.call(this);

  myFunction: function() {
    this.emit('foo');
  },
  urFunction: function() {
    this.emit('bar');
  }
};

myClass.prototype = Object.create(EventEmitter.prototype);
// or // util.inherits(myClass, EventEmitter);
var myObject = new myClass; // Shouldn't be necessary for my single-instance case

Or adding my functions/prototypes to something constructed like so:

var EventEmitter = require('events').EventEmitter;
var util = require('util');

var myObject = new EventEmitter();
// or // var myObject = Object.create(new EventEmitter); // Dunno difference
myObject.myFunction = function() {
    this.emit('foo');
  },
myObject.urFunction = function() {
    this.emit('bar');
  }
};

util.inherits(myObject, EventEmitter);

How do I allow myObject to emit events, while keeping the object literal notation? So many confusing ways to do it, but not one within those JSON-like notated objects.

Redsandro
  • 11,060
  • 13
  • 76
  • 106
  • Very good question, I like using Object.create() and literals from browser based environments but using node js for a while now, my conclusion is that ES5 like object handling is somewhat underused. Reason might be that functions like util.inherits do not exist for handling object literals. – vanthome Mar 27 '13 at 13:31
  • Please see this answer: http://stackoverflow.com/questions/24925115/how-to-add-event-ability-to-object-instance-in-node/24934255#24934255 – victorwoo Jul 25 '14 at 06:04

3 Answers3

9

Why not use composition instead of inheritance?

var myObject = {
  myFunction: function() {
    console.log('foo');
  },
  urFunction: function() {
    console.log('bar');
  },
  emitter: new EventEmitter()
}
ziad-saab
  • 19,139
  • 3
  • 36
  • 31
  • This looks interesting. Is the 'internal' emitter equally 'public' as when `myObject` would have been the emitter? E.g. I can do from within a root function `self.myObject.emitter.on('customEvent', doSomethinG());` (provided ofcourse `self = this` is set in the same root)? – Redsandro Jun 01 '12 at 13:14
  • 2
    Yes. And if you want to make your object quack like an emitter, you can even add some of the emitter functions that you will use like `var myObject = { ... emitter: new EventEmitter(), emit: myObject.emitter.emit, on: myObject.emitter.on, ...} – ziad-saab Jun 01 '12 at 13:17
  • Clever. Just curious on how versatile this is: Can I somehow pass an emitter from a separate function/scope and have the same fun? E.g. a _nodejs request_ triggers `onRequest()` , which does something with `myObject` which causes events, which are listened to inside `onRequest()`. Now, a second (third and _n_ th) simultaneous request do the same, but should only listen to events caused by their own fiddling with `myObject`. This would require `onRequest()` to pass a new emitter to `myObject` everytime, otherwise `myObject`'s events will be caught by every running instance of `onRequest()`. – Redsandro Jun 01 '12 at 17:32
  • I'm not sure I am grasping 100% of your last question. But look at it this way: you're inheriting of all the features of `EventEmitter`. But instead of inheriting them by extending `EventEmitter`, you're inheriting them by adding an `EventEmitter` as part of your object. – ziad-saab Jun 01 '12 at 18:11
8

Unfortunately, your desired object-literal syntax is not possible. It's because

var obj = {};

is equivalent to

var obj = Object.create(Object.prototype);

in that obj's prototype is fixed to Object.prototype at creation and you can't change that later on.

Since you just want a single instance, I'd argue that it does make sense to create an instance of EventEmitter and assign properties to it:

var EventEmitter = require('events').EventEmitter;
var obj = new EventEmitter();

obj.doStuff = function() {
  this.emit('stuff');
};

You don't need util.inherits here, as you just have one instance, so there's no chain to setup.

J. Ryan Stinnett
  • 801
  • 8
  • 13
  • Thanks for explaining the part about the `prototype`. I only partially understood what was going on, and thought {} was a shorthand for defining and instantiating a constructor-less object. Do you know a more native way of assigning properties to the `EventEmitter` instance so that I can more easily port my `object literals` to them, as referenced in my first comment to Mattias Buelens? – Redsandro Jun 01 '12 at 13:17
  • 1
    I am not aware of a _native_ way to extend via object literal. However, include jQuery is not ideal for a node.js project, since most of jQuery is about manipulating the DOM. I would suggest including a utility library like [Lo-Dash](http://lodash.com/) or [Underscore](http://underscorejs.org/). You can install this with npm. – J. Ryan Stinnett Jun 01 '12 at 19:04
  • Very good explanation but your proposed solution is not sensible if you create a reusable library. On that case you will not want to add functions after construction. – vanthome Mar 27 '13 at 13:29
  • If jQuery hast the thing, couldn't you just try and find the relevant parts from the jQuery js file and copy only those? – Julix Aug 25 '18 at 17:43
-1

This is a completely wild guess, as I have only coded JavaScript for browsers. However, can't you just call EventEmitter on your object after creating your object literal?

myObject = {
    myFunction: function() {
        console.log('foo');
    },
    urFunction: function() {
        console.log('bar');
    }
};
EventEmitter.call(myObject);
Mattias Buelens
  • 19,609
  • 4
  • 45
  • 51
  • I wish it was that simple, but unfortunately it doesn't work. A dirty way that mimics turning my `object literal` into an `EventEmitter` is by borrowing [jQuery's extend()](http://api.jquery.com/jQuery.extend/) like so: `var myObject = new EventEmitter(); $.extend(myClass, myObject);` where `myClass` is my `object literal`. Obviously I'm looking for a more native way. – Redsandro Jun 01 '12 at 00:55