Home >Web Front-end >JS Tutorial >JavaScript prototypes and inheritance
1. Function creation process
Before understanding the prototype chain, let’s first take a look at what a function does during the creation process. Let’s take an example of an empty function:
1 function A() {};
When we Declaring such an empty function in the code, the essence of js parsing is (superficial understanding needs to be deepened):
1. Create an object (with constructor attribute and [[Prototype]] attribute). According to ECMA, [[Prototype]] The attributes are invisible and non-enumerable
2. Create a function (with name and prototype attributes), and then reference the object just created through the prototype attribute
3. Create variable A, and assign the reference of the function to variable A
As shown in the figure below:
(note that the figures are all "reference" types)
The creation of each function goes through the above process.
2. Constructor
So what is a constructor?
According to the definition of ECMA
Constructor is a function that creates and initializes the newly created object.
Constructor is a function that is used to create and initialize a new object at the same time.
What kind of function can be used to create and initialize new objects at the same time? The answer is: any function, including empty functions.
So, the conclusion is: any function can be a constructor.
3. Prototype
According to the previous empty function creation diagram, we know that each function automatically adds the prototype attribute when it is created. This is the prototype of the function. From the picture, we can see that its essence is a reference to an object ( This object is temporarily named prototype object).
We can operate on the prototype object of the function, just like an ordinary object! Let’s confirm it together.
Add some code around the empty function you just created:
function A() {
this.width = 10;
this.data = [1,2,3];
this.key = "this is A";
}
A._objectNum = 0;//Define the attributes of A
A.prototype.say = function(){//Add attributes to the prototype object of A
alert("hello world")
}
Lines 7 to 9 of code add a say attribute to the prototype object of the function and reference an anonymous function. According to the "function creation" process, the diagram is as follows:
(The gray background is the attribute added based on the empty function)
Simply put, the prototype is an attribute of the function, which is automatically added by the js compiler during the creation process of the function.
So what is the use of prototypes?
First understand the new operator, as follows:
1
2 var a1 = new A;
var a2 = new A;
This is how to create objects through constructors, so why create objects in this way instead of directly var What about a1 = {};? This involves the specific steps of new. The new operation here can be divided into three steps (taking the creation of a1 as an example):
1. Create a new object and assign it to the variable a1: var a1 = {};
2. Put this object The [[Prototype]] attribute points to the prototype object of function A: a1.[[Prototype]] = A.prototype
3. Call function A and point this to the object a1 created in 1 to initialize the object: A. apply(a1,arguments)
The structure diagram is as follows:
As you can see from the picture, whether it is object a1 or a2, there is an attribute that saves a reference to the prototype object of function A. For these objects, some common methods can be found in the prototype of the function, saving memory. space.
4. Prototype chain
After understanding the new operator and the role of prototype, let’s take a look at what [[Prototype]] is? And how does the object search for attributes along this reference?
In the world of js, each object has a [[Prototype]] attribute by default, and the address it stores constitutes the prototype chain of the object. It is automatically added by the js compiler when the object is created. Its value is determined by the right-hand parameter of the new operator: when we var object1 = {};, object1's [[Prototype]] points to the prototype object of the Object constructor, because var object1 = {}; is essentially equal to var object = new Object(); (For the reason, please refer to the above analysis process of new A).
When the object is looking for a certain attribute, it will first traverse its own attributes. If there is no such attribute, it will continue to search for the object referenced by [[Prototype]]. If there is no more, it will continue to search for the reference of [[Prototype]].[[Prototype]]. object, and so on, until [[Prototype]]...[[Prototype]] is undefined (Object's [[Prototype]] is undefined)
As shown in the picture above:
//We want to get a1.fGetName
alert(a1.fGetName);//Output undefined
//1. Traverse the a1 object itself www.2cto.com
//The result is that the a1 object itself does not have the fGetName attribute
//2. Find the [[Prototype]] of a1, also It is its corresponding object A.prototype, and traverses it at the same time
//The result is that A.prototype does not have this attribute either
//3. Find the [[Prototype]] of the A.prototype object and point to its corresponding object Object.prototype
/ /As a result, Object.prototype does not have fGetName
//4. Trying to find the [[Prototype]] attribute of Object.prototype, and the result returns undefined. This is the value of a1.fGetName
Simply put, it is saved through the [[Prototype]] of the object A reference to another object, through which attributes are searched upwards. This is the prototype chain.
5. Inheritance
With the concept of prototype chain, inheritance can be carried out.
1 function B() {};
At this time, the prototype B.prototype of B is generated
The prototype itself is an Object object, we can see what data is placed in it
B.prototype is actually {constructor: B, [ [Prototype]] : Object.prototype}
Because prototype itself is an instance of an Object object, its prototype chain points to the prototype of Object
B.prototype = A.prototype;//It is equivalent to pointing the prototype of B to A The prototype of B's prototype chain points to A, how to implement it?
The first is to change the prototype chain reference address
1 B.prototype.__proto__ = A.prototype;
There is no __proto__ method in ECMA. This is added by js interpreters such as ff and chrome, which is equivalent to EMCA [[Prototype]], this is not a standard method, so how to use the standard method?
We know that when operating new, we actually just point the prototype chain of the instance object to the prototype address block of the constructor, then we can operate like this
1 B.prototype = new A();
The result is:
Generate an instance of A and assign it to the prototype of B, that is, B.prototype is equivalent to the object {width: 10, data: [1,2,3], key: "this is A", [[Prototype]]: A.prototype}
In this way, the prototype of A is saved through the object attribute B.prototype.[[Prototype]], forming a link to the prototype
But note that the constructor of the object generated by B has changed, because in There is no constructor attribute in B. You can only find A.prototype from the prototype chain and read out constructor:A
var b = new B;
console.log(b.constructor);//output A
So we have to manually set it back B itself
B.prototype.constructor = B;
//Now the prototype of B becomes {width: 10, data: [1,2,3], key: "this is A", [[Prototype]] : A.prototype, constructor: B}
console.log(b.constructor);//output B
//At the same time, B directly inherits A’s custom attributes width and name through the prototype
console.log(b.data) ;//output [1,2,3]
//The disadvantage of this is that
b.data.push(4);//Directly changes the data array (reference) of the prototype
var c = new B;
alert( c.data);//output [1,2,3,4]
//In fact, what we want is just the prototype chain. We want to define the custom properties of A in B (not in prototype)
// How to carry out inheritance?
//Since we don’t want the custom attributes in A, we can find a way to filter them out
//We can create a new empty function
function F(){}
//Point the prototype of the empty function to the constructor A Prototype
F.prototype = A.prototype;
//At this time, use the new operation to point the prototype chain of B.prototype to the prototype of F
B.prototype = new F;
//At this time, the prototype of B becomes {[[Prototype]]: F.prototype}
//Here F.prototype is actually just a reference to an address
//But the constructor of the instance created by B points to A, so Here we need to display and set the constructor attribute of B.prototype
B.prototype.constructor = B;
//At this time, the prototype of B becomes {constructor: B, [[Prototype]]: F.prototype}
//This way The prototype inheritance of B to A is realized. The diagram is as follows, where the red part represents the prototype chain: