Search code examples
javascriptconstructorprototypeprototypal-inheritance

Can I apply a prototype to a js constructor (as opposed to the instances it creates)?


I am attempting to use constructor created objects with prototypes in javascript.

I would like to apply a prototype to the constructor so that any objects it creates inherit the prototype automatically. Is this possible?

I only seem to be able to apply the prototype to individual instances created by the constructor, and not to the constructor itself.


I am aiming for something like this:

var animal = {
    speak: function(){ console.log(this.name + ' says ' + this.sound); }
}    

var dog = function(name){
    this.name = name;
    this.sound = 'bark';
}
dog.__proto__ = animal;

var pig = function(name){
    this.name = name;
    this.sound = 'oink';
}
pig.__proto__ = animal;

var percy = new pig("percy");
percy.speak();

var fido = new dog("fido");
fido.speak();

The above gives me the error: percy.speak is not a function.

It seems that the __proto__ declaration is not being applied to pig or dog.

In contrast, it DOES let me apply the __proto__ declaration to percy or fido directly:

var animal = {
    speak: function(){ console.log(this.name + ' says ' + this.sound); }
}


var dog = function(name){
    this.name = name;
    this.sound = 'bark';
}

var pig = function(name){
    this.name = name;
    this.sound = 'oink';
}

var percy = new pig("percy");
percy.__proto__ = animal;
percy.speak();

This works, and gives me percy says oink, as expected.


Solution

  • First, never use __proto__. Use Object.getPrototypeOf or Object.setPrototypeOf.

    __proto__ is a non-standard accessor which should have never existed and may not work if your object does not inherit from Object.protoype or __proto__ has been shadowed. Moreover, changing the [[Prototype]] of an object has a big impact in performance, see the warning in MDN.

    Second, you are not supposed to mess with [[Prototype]]s when adding methods.

    Add them to the prototype instead.

    var animalProto = {
      speak: function(){ console.log(this.name + ' says ' + this.sound); }
    };
    var dog = function(name){
      this.name = name;
      this.sound = 'bark';
    };
    dog.prototype = animalProto;
    
    var pig = function(name){
      this.name = name;
      this.sound = 'oink';
    };
    pig.prototype = animalProto;
    
    var percy = new pig("percy");
    percy.speak();
    
    var fido = new dog("fido");
    fido.speak();

    But it would be cleaner using classes:

    class Animal {
      constructor(name, sound) {
        this.name = name;
        this.sound = sound;
      }
      speak() {
        console.log(this.name + ' says ' + this.sound);
      }
    }
    class Dog extends Animal {
      constructor(name) {
        super(name, 'bark');
      }
    }
    class Pig extends Animal {
      constructor(name) {
        super(name, 'oink');
      }
    }
    
    var percy = new Pig("percy");
    percy.speak();
    
    var fido = new Dog("fido");
    fido.speak();