Say we have a class defined as below.
class Person {
constructor(name, energy) {
this.name = name;
this.energy = energy;
}
eat(v) {
this.energy += v;
}
sleep(v) {
this.energy += v;
}
}
const p1 = new Person("Jack", 1);
const p2 = new Person("Mary", 2);
It's a normal class, let's explore some of its details.
A class definition has a prototype property, which is just a normal object.
> Person.prototype
{constructor: ƒ, eat: ƒ, sleep: ƒ}
The eat
and sleep
property is just the function we defined inside the Person
class, so we know now that these functions are stored in the prototype object.
The constructor
property points to the whole class definition, so we have below equalness.
Person.prototype.constructor === Person; // true
Every object in JavaScript have a property __proto__
which points to the object's prototype, so does this prototype object. Now you may ask, what is the prototype of the prototype? The answer is Object
's prototype.
Person.prototype.__proto__ === Object.prototype; // true
This Person class definition itself also have a __proto__
property, which points to the native code implementation.
> Person.__proto__
ƒ () { [native code] }
So, to sum it up, this Person
class has some details like below.
Person
- prototype
- constructor => Person
- eat
- sleep
- __proto__ => Object.prototype
- __proto__ => ƒ () { [native code] }
After exploring this Person
class, let see its instance p1
and p2
. These 2 are just plain object, so they doesn't have the prototype property. But it also has __proto__
property, which points to the Person
's prototype.
p1.__proto__ === Person.prototype; // true
p2.__proto__ === Person.prototype; // true
So when these instance call the class methods, they actually try the call methods stored in the class's prototype object.
Now let explore upward. We learn from above that the Person
's __proto__
property points to Object.prototype
. What is Object? Let's see some details.
First let's check the __proto__
property in Object.
> Object.__proto__
ƒ () { [native code] }
Oh, now we know Object is just a built-in function. Function is just like class, has an prototype property.
Wow, we can see there are a lot going on in the Object's prototype, such as toString
. The object we normally see are all in the Object prototype chain, that's why we can always run the toString
function.
The Object's prototype object also have a __proto__
property, let see what it is.
Object.prototype.__proto__ === null; // true
Oh, now we know the root of this chain is null.
So to sum it up, from bottom to top, we have details like below.
p1
- __proto__ => Person.prototype
p2
- __proto__ => Person.prototype
Person
- prototype
- constructor => Person
- eat
- sleep
- __proto__ => Object.prototype
- __proto__ => ƒ () { [native code] }
Object
- prototype
- __proto__ => null
- toString
- ...
- __proto__ => ƒ () { [native code] }
null
Actually, the es6 class syntax is just a syntax sugar above the function style. So the Person
class is almost the same as below function implementation.
function Person(name, energy) {
this.name = name;
this.energy = energy;
}
Person.prototype.eat = function(v) {
this.energy += v;
}
Person.prototype.sleep = function(v) {
this.energy += v;
}
const p1 = new Person("Jack", 1);
const p2 = new Person("Mary", 2);
In above example, we walk through the normal class object with the prototype chain. Now let's take the array as an example to see how prototype chain works in built-in function.
Normally, if we want to create an array, we will do below.
const arr = [];
This is actually a syntax sugar for below code.
const arr = new Array();
Now we have this array instance, let check its __proto__
.
arr.__proto__ === Array.prototype; // true
Just as expected, this arr instance's __proto__
property points to the Array
definition's prototype property.
Now let's see this Array
definition.
> Array.__proto__
ƒ () { [native code] }
As you can see, this Array
is just a function. Let's see its prototype.
A lot of things going on here, that's why we have a lot of methods to call with array instance.
Let's explore upward to see its prototype's prototype.
Array.prototype.__proto__ === Object.prototype; // true
Yes, Array
is also under the Object
's prototype.
So with all this information, from bottom to up, we can have below information.
arr
- __proto__ => Array.prototype
Array
- prototype
- constructor => Array
- push
- pop
- ...
- __proto__ => Object.prototype
- __proto__ => ƒ () { [native code] }
Object
- prototype
- __proto__ => null
- toString
- ...
- __proto__ => ƒ () { [native code] }
null