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.