Home > Software engineering >  How to add events to a derived/child class in NodeJS?
How to add events to a derived/child class in NodeJS?

Time:11-14

I have a child class which extends parent class. I also want child class to be able to emit events. Examples I found were extending EventEmitter. But JS does not allow multiple inheritance. I tried to implement it via mixin but not sure how to do without converting my parent class to a mixin.

Example

Say there is a base class for vehicles all vehicles move and some can emit events when they move

class Vehicle {
    constructor(name) { this.name = name }
    move() { console.log(this.name, 'Moved'); }
}

class Car extends Vehicle {
    constructor() { super('Ford'); }
    move() {
        super.move();
        this.engineSound();
    }
    engineSound() {
        console.log("Vroooom!");
    }
    // some other car related properties
}

class Ambulance extends Vehicle { // want to extend EventEmitter aswell
    constructor() { super('Ambulance1'); }
    move() {
        super.move();
        // emit moved event here
    }
}

let normalVehicle = new Vehicle('Normal');
normalVehicle.move();

let car = new Car();
car.move();

let ambulance = new Ambulance();
// Listen for moving ambulance here
// ambulance.on('moved', () => { console.log('Ambulance coming') });
ambulance.move();

But to emit events docs suggest extending EventEmitter but JS does not support extending multiple classes.

Saw few examples which were using Object.assign but not sure how that can be applied here.

CodePudding user response:

es6 class syntax doesn't support multiple inheritance at all. You can try weird trickery if you convert all classes to es5 constructor functions and then use things like Object.assign. But the best approach would be to use composition over inheritance.

OOP is used to try and mimic real world objects, so in real world terms it also doesn't make sense for an ambulance to be itself an event emitter.

However, using a composition approach, it makes sense for an ambulance to have an event emitter, like a radio or other such transmission device:

class Ambulance extends Vehicle {
    #radio = new EventEmitter();
    constructor() {
      super("Ambulance");
    }
    emit(event) { this.#radio.emit(event) }
    on(event, handler) { this.#radio.on(event, handler) }
    move() {
        super.move();
        this.emit("moved");
    }
}
let ambulance = new Ambulance();
ambulance.on('moved', () => { console.log('Ambulance coming') });
ambulance.move();

composition example

Note that the radio is private (using the new JS private fields feature), and I only supply a facade for the exact functionality I want the ambulance users to have, in this case 'on' and 'emit'.


With that said...

Lets get crazy and actually answer your question! Again, this will require to use es5 constructor functions:

// Base class - no inheritance
var Vehicle = (function () {
  function Vehicle(name) {
    this.name = name;
  }
  Vehicle.prototype.move = function () {
    return this.name   " Moved\n";
  };
  return Vehicle;
})();

// Derived from Vehicle - regular inheritance from a single object
var Car = (function (_super) {
  function Car() {
    // Calling super() in constructor
    _super.call(this, "Ford");
  }
  // extend Vehicle
  Car.prototype = Object.create(Vehicle.prototype);
  // bring back Car's constructor, because last step overrid it
  Object.defineProperty(Car.prototype, "constructor", {
    value: Car,
    enumerable: false,
    writable: true,
    configurable: true
  });
  Car.prototype.move = function () {
    return _super.prototype.move.call(this)   this.engineSound();
  };
  Car.prototype.engineSound = function () {
    return "Vroooom!\n";
  };
  return Car;
})(Vehicle);

// Derived from Vehicle and EventEmitter - multiple inheritance!
var Ambulance = (function (_superVehicle, _superEventEmitter) {
  function Ambulance() {
    _superVehicle.call(this, "Ambulance");
    _superEventEmitter.call(this);
  }

  // Create joint prototype using Object.assign
  var jointPrototype = Object.assign(
    {},
    _superEventEmitter.prototype,
    _superVehicle.prototype
  );
  // Connecting Ambulance to prototype chain
  Ambulance.prototype = Object.create(jointPrototype);
  // restore Ambulance's constructor
  Object.defineProperty(Ambulance.prototype, "constructor", {
    value: Ambulance,
    enumerable: false,
    writable: true,
    configurable: true
  });
  Ambulance.prototype.move = function () {
    this.emit("moved");
    return _superVehicle.prototype.move.call(this);
  };

  return Ambulance;
})(Vehicle, EventEmitter);

es5 multiple inheritance example

  • Related