Alan Souza
Alan Souza

Reputation: 7795

Prototypical inheritance: how to get instanceof?

After watching this video I learned about a nice way to implement inheritance with Javascript: prototypical inheritance. This method uses Object.create/Object.assign to create a new object based on another instance. This seems very interesting given that it is very easy to understand what is going on. For example:

const human = {
  species: 'human',
  create: function(values) {
    const instance = Object.create(this);
    Object.assign(instance, values);
    return instance;
  },
  sayName: function() {
    console.log(`Hi, my name is ${this.name}`);
  },
  saySpecies: function() {
    console.log(`Hi, I'm a ${this.species}`);
  }
}

const musician = human.create({
  species: 'musician',
  playInstrument: function() {
    console.log(`I'm a musician and I play ${this.instrument}`);
  }
});

const aHuman = human.create({ name: 'Paul' });
aHuman.sayName();
aHuman.saySpecies();

const aMusician = musician.create({ name: 'David', instrument: 'Guitar' });
aMusician.sayName();
aMusician.saySpecies();
aMusician.playInstrument();

// how to check if musician is an instance of human?
console.log(musician instanceof human);

With the classical inheritance, I can use instanceof to know if a given object has a given proto in its chain (e.g. musician instanceof Human).

This is what the proto chain looks like, which seems very similar to the one created with the new operator.

enter image description here

Question How to achieve the same thing with prototypical inheritance?

Upvotes: 0

Views: 149

Answers (3)

Alan Souza
Alan Souza

Reputation: 7795

@JLRishe's answer works. But if you want to avoid using functions and keep using objects you can implement your own version of instanceof. This code snippet also works:

const human = {
  species: 'human',
  create: function(values) {
    let newObj = Object.create(this);
    return Object.assign(newObj, values);
  },
  instanceof: function(obj) {
    if (!Object.getPrototypeOf(this)) {
      return false;
    }
    let isPrototypeOf = (
      this === obj ||
      Object.getPrototypeOf(this) === obj ||
      this === Object.getPrototypeOf(obj) ||
      Object.getPrototypeOf(this) === obj.prototype
    );
    if (!isPrototypeOf) {
      isPrototypeOf = this.instanceof.call(Object.getPrototypeOf(this), obj);
    }
    return isPrototypeOf;
  },
  sayName: function() {
    console.log(`Hi, my name is ${this.name}`);
  },
  saySpecies: function() {
    console.log(`Hi, I'm a ${this.species}`);
  }
}

const musician = human.create({
  species: 'musician',
  playInstrument: function() {
    console.log(`I'm a musician and I play ${this.instrument}`);
  }
});

const aHuman = human.create({ name: 'Paul' });
aHuman.sayName();
aHuman.saySpecies();

const aMusician = musician.create({ name: 'David', instrument: 'Guitar' });
aMusician.sayName();
aMusician.saySpecies();
aMusician.playInstrument();

var test = {};

console.log(human.instanceof(human));
console.log(musician.instanceof(human));
console.log(musician.instanceof(Object));
console.log(aHuman.instanceof(human));
console.log(aMusician.instanceof(human));
console.log(aHuman.instanceof(musician));
console.log(aHuman.instanceof(Object));
console.log(aMusician.instanceof(musician));
console.log(musician.instanceof(test));
console.log(human.instanceof(test));
console.log(aMusician.instanceof(test));
console.log(aHuman.instanceof(test));

Upvotes: 0

JLRishe
JLRishe

Reputation: 101662

There are two main problems with your attempt:

  • The RHS of instanceof has to be a function, so your types have to be built on top of functions.
  • You are not distinguishing between static/type methods and instance methods. create is a type method. sayName is an instance method.

The follow should work as you are expecting.

function Human(){}

Object.assign(Human, {
  create: function(values) {
    let newObj = Object.create(this.prototype);
    return Object.assign(newObj, values);
  }
});

Object.assign(Human.prototype, {
  species: 'human',
  sayName: function() {
    console.log(`Hi, my name is ${this.name}`);
  },
  saySpecies: function() {
    console.log(`Hi, I'm a ${this.species}`);
  }
});

function Musician(){}

Object.assign(Musician, Human);

Musician.prototype = Human.create({
  species: 'musician',
  playInstrument: function() {
    console.log(`I'm a musician and I play ${this.instrument}`);
  }
});

const aHuman = Human.create({ name: 'Paul' });
aHuman.sayName();
aHuman.saySpecies();

const aMusician = Musician.create({ name: 'David', instrument: 'Guitar' });
aMusician.sayName();
aMusician.saySpecies();
aMusician.playInstrument();

// how to check if musician is an instance of human?
console.log(aMusician instanceof Human);

Upvotes: 1

melpomene
melpomene

Reputation: 85767

Your code doesn't use inheritance. The only place where a new object is created is in this line:

    return Object.assign({}, this, values);
//                       ^^ here

{} creates a new object that inherits from Object.prototype. It's like doing Object.create(Object.prototype) or new Object().

Nothing in your code inherits from human or musician.

Upvotes: 1

Related Questions