2

I'm trying to inherit class "EventEmitter" and a pre defined class "Person", here is the code

class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age; 
    }
    introduces() {
        return `My name is ${this.name}. I am ${this.age} years old.`;
    }
}; 

\\here comes the mixin part

function mix(...mixins) {
    class Mix {}
    for (let mixin of mixins) {
        copyProperties(Mix, mixin);
        copyProperties(Mix.prototype, mixin.prototype);
    }
    return Mix;
}

function copyProperties(target, source) {
    for (let key of Reflect.ownKeys(source)) {  
        if (key !== "constructor" &&  key !== "prototype" && key !== "name") {
            let desc = Object.getOwnPropertyDescriptor(source, key);
            Object.defineProperty(target, key, desc);
        }
    }
}

I intend to create a new class 'PersonWithEmitter', and still call the constructor like below:

class PersonWithEmitter extends mix(Person,EventEmitter){
    constructor(name,age){
        super(name,age)
        \\do something else
    }

Here comes the issue, when I create a new instance of 'PersonWithEmitter' like this let someOne = new PersonWithEmitter("Tom",21), will not get what I what, In the new class, I want to use this.name and this.age, which is still undefined. So how can I change my code, So the new class can both have its parent's methods and only class "Person"'s constructor? Pardon me for my broken English.

8
  • 2
    Don't write entirely in bold. Commented Aug 17, 2017 at 16:10
  • Why not used an existing implementation like npmjs.com/package/event-emitter-mixin Commented Aug 17, 2017 at 16:12
  • @loganfsmyth I think this question is more about why it doesnt work than how to implement this. Clearly, implementing something like this isn't the issue, it's how the mixin works in JS thats at quesion here... Commented Aug 17, 2017 at 16:13
  • 1
    Never hurts to confirm. Commented Aug 17, 2017 at 16:15
  • @somethinghere since this is my first time use mixin and EventEmitter,I just cannot figure out the right way to implement this. Commented Aug 17, 2017 at 16:16

1 Answer 1

3

In many cases multiple inheritance in JavaScript indicates wrong design decision. It may result in hacky objects that don't behave as they should. The way it is done should always be determined by particular objects. In some cases a shallow copy of own properties is needed, in another the entire prototype chain should be traversed. Composition over inheritance is often a better choice.

The problem in the code above is that class constructors are not called. Mix has empty constructor. This is the reason why PersonWithEmitter doesn't work as expected.

Multiple constructor function calls can generally be stacked like:

function Foo(...args) {
  let _this = this;
  _this = Bar.apply(_this, args);
  _this = Baz.apply(_this, args);
  return _this;
}

This won't work if Bar or Baz is ES6 class because it contains a mechanism that prevents it from being called without new. In this case they should be instantiated:

function Foo(...args) {
  copyProperties(this, new Bar(...args));
  copyProperties(this, new Baz(...args));
}

Static and prototype properties may also be copied to Foo like is shown in code above.

If the case is narrowed down to Node.js EventEmitter, it can be handled like a special case. Its implementation is certain and stable. It is already known that EventEmitter does initialization in constructor, it has a shallow prototype chain and property descriptors. So it likely should be:

class Foo extends Bar {
  constructor(...args) {
    super(...args)
    EventEmitter.call(this);
    // or
    // EventEmitter.init.call(this);
}
copyProperties(Foo.prototype, EventEmitter.prototype);
Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, problem solved. The way I see it now, I can do it in two way, First, I can just let class Person inherit EventEmitter, and it did works. Second, I can call EventEmitter in the constructor as you write above.
You're welcome. Yes, in the case with EventEmitter both options are valid.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.