JavaScript Execution Mechanism (5) Clarifying Prototype Chain with Formulas

In the previous articles, we explained scope and closure from the perspective of the call stack and execution context, and at the same time talked about the difference between scope and this. Now the most difficult concepts to understand in JavaScript are only a prototype chain.

If we have read “JavaScript Advanced Programming”, we should have an impression of the prototype chapter. It has a very complex diagram through which the “proto”, “prototype”, and “constructor” of JavaScript are explained.

At the beginning of reading that book, I seemed to understand this part, and finally took this opportunity to reorganize to figure it out.

The content of this blog is still divided into two parts:

  • Clarify the pointing relationship of proto, prototype and constructor through several formulas.
  • Use of prototype chain

Clarify the prototype chain with a formula

First, the previous code

1
2
function Parent() {}
const p1 = new Parent();

The prototype chain relationship of this code is shown in the following figure

Where Parent () refers to the constructor function

https://res.cloudinary.com/dvtfhjxi4/image/upload/v1608035556/origin-of-ray/微信截图_20201215203152_tccbro.png

How is it, is it a little dizzy?

But don’t worry, we analyze the pointing relationship one by one through the formula.

Before this, there are a few pre-knowledge that you need to understand:

  • All variables in JavaScript are objects, divided into two categories, ordinary objects, which are Objects, and function objects, which are Functions
  • Any function can be a constructor function, as long as it is used after new, such as the Parent function above, if it does not pass new Parent (), it is an ordinary function, but after new, it is called a constructor function
  • proto’, ‘constructor’ are unique to objects
  • ‘prototype’ is unique to function and is a normal object

prototype

https://res.cloudinary.com/dvtfhjxi4/image/upload/v1608035555/origin-of-ray/微信截图_20201215203207_eihbzw.png

Prototype is a property unique to function, pointing from a ** function to a normal object, representing that the object is the prototype object of function, and this object is also the prototype object of the instance created by the current function **

At the beginning of prototype design is to achieve inheritance, so that all instances created by a specific function share properties and methods. It can also be said that all objects instantiated by a constructor function find common properties and methods. With prototype, we do not need to Create duplicate properties and methods for each instance, but create property methods on the prototype object that constructs the function

From this, we come to the first formula of this article, or conclusion:

< U > ** Each function object has a pointer prototype to its own prototype object, which is a normal object **

__proto__

https://res.cloudinary.com/dvtfhjxi4/image/upload/v1608035556/origin-of-ray/微信截图_20201215203215_aj5gbh.png

The proto property is found on all objects, including the function object, ** which points from one object to another, i.e. from one object to the object’s prototype **.

Just now we said that the properties and methods on Parent.prototype are called prototype properties and prototype methods, which can be accessed by all instances.

How does the instance relate to the prototype object? This is the second formula of this article:

1
p1.__proto__ = Parent.prototype

proto is called the implicit prototype, prototype is called the display prototype, and the prototype chain is also formed by the pointing of proto.

Our more general way of writing the above formula is:

1
2
const a = new A();
a.__proto__ = A.prototype(Object.prototype.__proto__ = null special)

In this way, we can replace a with A, where a is an ordinary object and A is its constructor function.

Through the above formula, we can analyze the pointing problem in the above figure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const p1 = new Parent(); // 因为
p1.__proto__ = Parent.prototype; // 所以

const Parent = new Function();
Parent.__proto__ = Fuction.prototype;

Function = new Object();
Function.__proto__ = Object.prototype

All prototypes are ordinary objects, that is, they come from new Object ()
Parent.prototype.__proto__ = Object.prototype;
Function.prototype.__proto__ = Object.prototype;

Because Object, Function can be placed after new, it is also a constructor function, that is, Object = new Function ()
Object.__proto__ = Function.prototype
Function.__proto__ = Function.prototype

How about such an analysis, is it a formula to clarify the pointing problem of proto?

constructor

Constructor is a property only available to objects. It points from an object to a function, and this function is the constructor function of the object.

https://res.cloudinary.com/dvtfhjxi4/image/upload/v1608035556/origin-of-ray/微信截图_20201215203222_hiqpqf.png

Here we derive the third formula of this article.

1
2
3
const a = new A();
a.constructor = A;
A.prototype is actually an instance of A//this should be paid very attention to

By this conclusion, the above figure is easy to analyze.

Summary

Here is a summary of all the formulas and conclusions of this article. After reading it, you can go back and look at the picture at the beginning to see if you can analyze it

  • All variables in JavaScript are objects, divided into two categories, ordinary objects, which are Objects, and function objects, which are Functions
  • Any function can be a constructor function, as long as it is used after new, such as the Parent function above, if it does not pass new Parent (), it is an ordinary function, but after new, it is called a constructor function
  • proto’, ‘constructor’ are unique to objects
  • ‘prototype’ is unique to function and is a normal object
    Each function object has a pointer prototype to its own prototype object, which is a normal object
  • if const a = new A (); then a. ‘proto’ = A.prototype(Object.prototype.__proto__ = null special)
  • if const a = new A (); then a.constructor = A;

Prototype chain application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Father(){
this.property = true;
}
Father.prototype.getFatherValue = function(){
return this.property;
}
function Son(){
this.sonProperty = false;
}
//Inherit the Father
Son.prototype = new Father ();//Son.prototype is overridden, causing Son.prototype.constructor to be overridden as well
Son.prototype.getSonVaule = function(){
return this.sonProperty;
}
var instance = new Son();
alert(instance.getFatherValue());//true

This is a more common inheritance method

So why can we point to the getFatherValue method on Father.prototype on an instance of Son?

  • First, since instance = new Son (), the new operator creates a temporary object tempObj, then executes Son.call (tempObj), and finally returns the entire tempObj, thus mounting the property sonProperty on tempObj and assigning it to instance.
  • Originally Son.prototype is a normal object, that is, an instance of Son, and its prototype chain pointer proto points to a normal object, that is, at this time Son.prototype.proto = Object.prototype,再往上Object.prototype.proto The search for the prototype chain is over.
  • But we modified Son.prototype to an instance of Father, we will create a new normal object when executing Son.prototype = new Father (), and the object has fatherPrototype on it. So at this time’Son.prototype.proto = Father.prototype`。
  • At this time, if we can’t find getFatherValue () on instance, we go back to Son.prototype to find it, and if we can’t find it again, we will go up the prototype chain built by proto, and then we find Son.prototype.proto, which is Father.prototype, and getFatherValue () is exactly what we define here.
  • By the way, getSonVaule () is defined on Son.prototype, which is the instance of new Father ()

So the reason why we can point out sonPrototypt, fatherPrototype, getFatherValue (), getSonVaule () through instance is completely different.