Jonathan Ong
Jonathan Ong

Reputation: 20325

Mixin EventEmitter methods using __proto__

Given an arbitrary object, I want to make it an EventEmitter:

var obj = {}
// mixin eventemitter
obj.on('event', ...)
obj.emit('event')

Also, when I type in obj, I don't want it to show the EventEmitter methods as methods. ex via CLI:

> obj
{}

Thus, right now I'm doing:

function mixinEventEmitter(obj) {
  obj.__proto__ = EventEmitter.prototype
  return obj
}

But people say using __proto__ is an anti-pattern: Node.js - inheriting from EventEmitter

Am I doing it correctly? Do you have a better way of doing this? Thanks.

Upvotes: 4

Views: 1888

Answers (2)

rich remer
rich remer

Reputation: 3577

The problem with __proto__ isn't that you're using prototypes instead of constructors. The issue is that it's the wrong way to use prototypes. But you don't want a prototype. You want a mixin. Using __proto__ is a hack that avoids doing the work of creating a mixin. If you want a mixin, you have to do it manually, without prototypes.

var EventEmitter = require("events").EventEmitter,
    obj = {};

function emitter(obj) {
    // copy EventEmitter prototype to obj, but make properties
    // non-enumerable
    for (var prop in EventEmitter.prototype) {
        Object.defineProperty(obj, prop, {
            configurable: true,
            writable: true,
            value: EventEmitter.prototype[prop]
        });
    }

    // also, make sure the following properties are hidden
    // before the constructor adds them
    ["domain", "_events", "_maxListeners"].forEach(function(prop) {
        Object.defineProperty(obj, prop, {
            configurable: true,
            writable: true,
            value: undefined
        });
    });

    // call EventEmitter constructor on obj
    EventEmitter.call(obj);

    // return the obj, which should now function as EventEmitter
    return obj;
}

emitter(obj);
obj.on("event", console.log.bind(console));
obj.emit("event", "foo");

Upvotes: 1

ebohlman
ebohlman

Reputation: 15003

The usual way to do this is to use util.inherits (the linked documentation contains an example that's almost exactly what you want).

Upvotes: 2

Related Questions