Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prototype copy vs Object.create() vs new

I was using inheritance and I noticed that there are three ways to get the same result. What's the difference?

function Animal(){
}

Animal.prototype.doThat = function() {
    document.write("Doing that");
}

function Bird(){
}

// This makes doThat() visible
Bird.prototype = Object.create(Animal.prototype); // Solution 1
// You can also do:
// Bird.prototype = new Animal(); // Solution 2
// Or:
// Bird.prototype = Animal.prototype; // Solution 3

var myVar = new Bird();
myVar.doThat();

As you can see I proposed three solution. Each one of them make the method doThat() visible.

If I comment all of them there is an error indeed.

If I decomment only one of them the program works.

So... what's really the difference between the three solution?

like image 639
user1883212 Avatar asked Apr 19 '13 08:04

user1883212


1 Answers

Bird.prototype = Animal.prototype; // Solution 3

Since this assigned Animal.prototype directly to Bird.prototype, all changes you make to the latter will also be reflected in Animal and all other classes that inherit from it.

For example:

Bird.prototype.fly = function() {
    console.log('I can fly.');
}

var some_animal = new Animal();
some_animal.fly(); // this will work

This is certainly not what you want. Not only will every method of this subclass available for other subclasses, but if you override parent methods in subclasses, the subclass that was defined last will override the changes of the other subclasses.

In your question title you seem to refer to this as "prototype copy". This is not correct, since no copy is created. Animal.prototype and Bird.prototype will refer to one and the same object.


Bird.prototype = new Animal(); // Solution 2

This has been a common way to setup inheritance for a long time but it has two major drawbacks:

  • If Animal requires arguments, which do you pass? Passing random, meaningless arguments just to make the function call work seems to be bad design.
  • Animal might have side effects, like increasing an instance counter, but in this moment you actually didn't create a new instance (or don't want to), you just wanted to setup the inheritance.

Bird.prototype = Object.create(Animal.prototype); // Solution 1

That's the way you should do it now (until a better solution comes). All you really want to do is hook up Animal's prototype into the prototype chain of Bird. It avoids all the drawbacks of the previous solutions.

To make it work properly though, you also have to call the Animal constructor inside the Bird constructor. It's like a call to super in other programming languages:

function Bird(){
    Animal.call(this);
}

This ensure that any commands/changes made to a new Animal instance are applied to the new Bird instance.

It is also good practice to assign the correct value to the constructor property of the prototype. It usually refers to the function the prototype "belongs to", but in all of the solutions above, Bird.prototype.constructor will refer to Animal. So we have to do:

Bird.prototype.constructor = Bird;

This property is not used internally, so you only have to do this if your own code relies on it. However, if you create a library to be used by others, they will probably expect the property to have to correct value.

like image 127
Felix Kling Avatar answered Oct 10 '22 11:10

Felix Kling