Benefits of using `Object.create` for inheritance

I tried to circle around the new Object.create method that was introduced in ECMAScript 5.

Usually, when I want to use inheritance, I do something like this:

 var Animal = function(name) { this.name = name; } Animal.prototype.print = function() { console.log(this.name); } var Dog = function() { return Animal.call(this, 'Dog'); } Dog.prototype = new Animal(); Dog.prototype.bark = function() { console.log('bark'); } 

I just assigned the newly created Animal object to the Dog prototype, and everything works like a charm:

 var dog1 = new Dog(); dog1.print(); // prints 'Dog' dog1.bark(); // prints 'bark' dog1.name; //prints 'Dog' 

but people (without explanation) say that Dog.prototype = new Animal(); not a way to inherit, and that I should use the Object.create approach:

 Dog.prototype = Object.create(Animal.prototype); 

which also works.

What is the advantage of using Object.create or am I missing something?

UPDATE: Some say Dog.prototype = Animal.prototype; may also work. So now I'm completely confused

+48
javascript
Jun 30 '13 at 17:14
source share
4 answers

Further, I assume that you are only interested in why Object.create is preferred for setting inheritance.

To understand the benefits, let's first clarify what the class "class" is made in JavaScript. You have two parts:

  • Function constructor . This function contains all the logic for creating an instance of a "class", that is, specific code.

  • Object prototype . This is an object inherited by the instance. It contains all the methods (and other properties) that should be shared between all instances.

Inheritance establishes an is-a relationship, for example a Dog is Animal . How is this expressed in terms of a constructor function and a prototype object?

Obviously, the dog must have the same methods as the animal, that is, the prototype of the Dog object must somehow include the methods from the animal prototype object. There are several ways to do this. You will often see this:

 Dog.prototype = new Animal(); 

This works because the Animal instance is inherited from the Animal prototype object. But also implies that each dog is inherited from one particular instance of Animal . This seems to be a little strange. Shouldn't the specific instance code be used only in the constructor function? Suddenly, the specific instance code and prototype methods seem mixed.

At the moment, we really do not want to run the Animal instance code, we only need all the methods from the Animal prototype object. This is what Object.create allows us to do:

 Dog.prototype = Object.create(Animal.prototype); 

Here we do not create a new instance of Animal , we get only prototypes. The specific code is executed exactly where it should be, inside the constructor:

 function Dog() { Animal.call(this, 'Dog'); } 

The biggest advantage is that Object.create will always work. Using new Animal() only works if the constructor does not expect any arguments. Imagine the constructor looked like this:

 function Animal(name) { this.name = name.toLowerCase(); } 

You always need to pass the string to Animal , otherwise you will receive an error message. What will you go Dog.prototype = new Animal(??); when you do Dog.prototype = new Animal(??); ? It doesn't really matter which line you go through while skipping something, which I hope will show you that this is a bad design.




Some say that Dog.prototype = Animal.prototype; may also work. So now I'm completely confused

Everything that โ€œaddsโ€ properties from Animal.prototype to Dog.prototype will โ€œworkโ€. But the solutions are of different quality. In this case, you will have a problem that any method that you add to Dog.prototype will also be added to Animal.prototype .

Example:

 Dog.prototype.bark = function() { alert('bark'); }; 

Since Dog.prototype === Animal.prototype , all instances of Animal now have a bark method, which, of course, is not what you want.

Object.create (and even new Animal ) add one level of indirection to inheritance, creating a new object that inherits from Animal.prototype , and that the new object becomes Dog.prototype .




Inheritance in ES6

ES6 introduces a new syntax for creating design functions and prototypes that look like this:

 class Dog extends Animal { bark() { alert('bark'); } } 

This is more convenient than described above, but as it turned out, extends also uses the internal equivalent of Object.create to set up inheritance. See Steps 2 and 3 in the ES6 project.
This means that using Object.create(SuperClass.prototype) is the โ€œmore correctโ€ approach in ES5.

+111
Jun 30 '13 at 17:48
source share

First, starting an Animal constructor can have unwanted side effects. Consider this:

 var Animal = function(name) { this.name = name; Animal.instances.push(this); }; Animal.instances = []; 

This version will track all created instances. You do not want your Dog.prototype be written there.

Secondly, Dog.prototype = Animal.prototype is a bad idea, as it means that bark will become an Animal method.

+9
Jun 30 '13 at 17:42
source share

I will illustrate the difference a bit:

Here is what basically happens when you write new Animal() :

  //creating a new object var res = {}; //setting the internal [[prototype]] property to the prototype of Animal if (typeof Animal.prototype === "object" && Animal.prototype !== null) { res.__proto__ = Animal.prototype; } //calling Animal with the new created object as this var ret = Animal.apply(res, arguments); //returning the result of the Animal call if it is an object if (typeof ret === "object" && ret !== null) { return ret; } //otherise return the new created object return res; 

And this is what mainly happens with Object.create :

  //creating a new object var res = {}; //setting the internal [[prototype]] property to the prototype of Animal if (typeof Animal.prototype !== "object") { throw "...."; } res.__proto__ = Animal.prototype; //return the new created object return res; 

Thus, it does the same, but does not call the Animal function, and also always returns the newly created object. In your case, you get two different objects. Using the first method, you will get:

 Dog.prototype = { name: undefined, __proto__: Animal.prototype }; 

and with the second method you will get:

 Dog.prototype = { __proto__: Animal.prototype }; 

You really don't need to have the name property in your prototype because you have already assigned it to the Dog instance with Animal.call(this, 'Dog'); .

Your primary goal is to give the Dog instance access to all the properties of the Animal prototype, which is achieved by both methods. The first method, however, does some additional things that are not really needed in your case or may even cause the unwanted results that Pumbaa80 mentioned.

+7
Jun 30 '13 at 18:13
source share

Let only the code understand:

A.prototype = B.prototype;

 function B() {console.log("I am B");this.b1= 30;} B.prototype.b2 = 40; function A() {console.log("I am A");this.a1= 10;} A.prototype.a2 = 20; A.prototype = B.prototype; A.prototype.constructor = A; var a = new A; var b = new B; console.log(a);//A {a1: 10, b2: 40} console.log(b);//B {b1: 30, b2: 40} console.log(A.prototype.constructor);//A console.log(B.prototype.constructor);//A console.log(A.prototype);//A {b2: 40} console.log(B.prototype);//A {b2: 40} console.log(a.constructor === A); //true console.log(b.constructor === A); //true console.log(a.a2);//undefined 

enter image description here

A.prototype = Object.create (B.prototype);

 function B() {console.log("I am B");this.b1= 30;} B.prototype.b2 = 40; function A() {console.log("I am A");this.a1= 10;} A.prototype.a2 = 20; A.prototype = Object.create(B.prototype); A.prototype.constructor = A; var a = new A; var b = new B; console.log(a);//A {a1: 10, constructor: function, b2: 40} console.log(b);//B {b1: 30, b2: 40} console.log(A.prototype.constructor);//A console.log(B.prototype.constructor);//B console.log(A.prototype);//A {constructor: function, b2: 40} console.log(B.prototype);//B {b2: 40} console.log(a.constructor === A); //true console.log(b.constructor === B); //true console.log(a.a2);//undefined 

enter image description here

+5
Jan 30 '14 at 16:10
source share



All Articles