prototype, __proto__, object specifics, and sharing via prototype

https://hackernoon.com/prototypes-in-javascript-5bba2990e04b

Object.prototype is one of the rare objects that has no prototype: it does not inherit
any properties.

Step 1:

Every object in Javascript is derived from base Object. Base Object has many built-in properties, with its prototype pointing to an empty “Object prototype”. Hence, in memory we see “function Object” which has a prototype property referencing Object Prototype. Then it has a constructor reference coming back to Object.

Thus, when we create our literal object (with a name property in there), it derives from Object. Thus, it has its reference __proto__ pointing to Object Prototype.

That is why when you see an instance of Object (object literal) and Object, their prototype and constructor are the same.

Object.prototype –> {}
Object.prototype.constructor); –> [Function: Object]

objLiteral.__proto__ –> {}
objLiteral.__proto__.constructor –> [Function: Object]

Step 2 – Creating the Rabbit object

Now we create a Rabbit object. Therefore, by definition, Rabbit has a prototype reference pointing to an empty Rabbit Prototype. The Rabbit Prototype has a constructor pointing back to the Rabbit definition. But remember, every object in Javascript derives from Object. Hence Rabbit’s Prototype object is actually an object literal that has a __proto__ pointing to Object Prototype.

So as you see, Rabbit’s prototype and prototype.constructor is very straight forward. But it will be surprising to the reader that Rabbit.prototype has a __proto__ property. That is because Rabbit.prototype itself is an empty object literal. And like any object in JS, it derives from Object. Thus, that is why Rabbit.prototype.__proto__.constructor is [Function: Object].

Step 3 – Connecting the Rabbit object

So now, let’s connect the Rabbit’s prototype to some other object literal instead of the empty default one given to us. Say an object literal with property name, initialized to “not entered”. In memory it will looks like this:

As stated earlier, any object in JS has a __proto__ pointing to Object.prototype. Thus, the object literal we created also has this. When we connect our Rabbit.prototype to it, the Rabbit’s prototype takes on the object literal.


Rabbit.prototype –> { name: ‘not entered’ }
Rabbit.prototype.__proto__ –> {}
Rabbit.prototype.constructor –> [Function: Object]

An interesting thing to note here is that when evaluating Rabbit.prototype.constructor, since
Rabbit.prototype is { name: ‘not entered’ }, and we call constructor on it, it does not exist.
So what it does is that it tries to find property “constructor” on object’s __proto__, which is
Object.prototype. It exists (Object.prototye.constructor), and it points to [Function: Object].

Instantiations of Rabbit

Prototype Chain

From all the information presented above, all prototype objects (except Object.prototype) are normal objects that do have a prototype. All of the built-in constructors (and most user-defined constructors) have a prototype that inherits from Object.prototype.

For example, Date.prototype inherits properties from Object.prototype, so a Date object created by new Date() inherits properties from both Date.prototype and Object.prototype.

This linked series of prototype objects is known as a prototype chain.

The problem with Prototoypes

As prototype object is shared among all the objects created using the constructor function, it’s properties and methods are also shared among all the objects. If an object A modifies property of the prototype having primitive value, other objects will not be effected by this as A will create a property on its objects.

If you were to create many rabbit instances, and then change the property name in its __proto__, all other rabbits will be affected.

Solution – Combine Constructor/Prototype

We know 2 things:

  • Every object calls the constructor and thus, gets its own instance of the function definition, along with its own properties.
  • Modifying a Prototype property using one instance reflects the other instances also.

What we can do is to define all the object specific properties inside the constructor and all shared properties and methods inside the prototype as shown below:

Full Source to Rabbit example

OUTPUT

Rabbit {}
{}
[Function: Object]
[Function: Rabbit]
— object literal —
{}
[Function: Object]
———————-
{}
[Function: Object]
———————-
Rabbit.prototype = objLiteral
— Rabbit —
{ name: ‘not entered’ }
{}
[Function: Object]
———————-
{ name: ‘not entered’, copy: [Function] }
[Function: Object]
Rabbit Constructor
{ name: ‘not entered’, copy: [Function] }
[Function: Object]