1, Case introduction
Define a constructor for singers:
function Singer(name,gender,age) { this.name = name; this.gender = gender; this.age = age; this.sing = function(){ console.log('I can sing anything!'); } }
Then, two singer objects are instantiated:
var singer1 = new Singer('Deng Ziqi','famale',28); var singer2 = new Singer('tengger ','male',50); console.log(singer1); // Singer {name: 'Deng Ziqi', gender: 'falale', age: 28, sing: ƒ} console.log(singer2); // Singer {name: 'Tengger', gender: 'male', age: 50, sing: ƒ} singer1.sing(); // I can sing anything! singer2.sing(); // I can sing anything!
In the above code block, we instantiate two singer objects. We might as well imagine that when the number of instantiated objects increases, if the number of instantiated singers increases to 10000, each person will create a sing method, which will occupy a large part of memory.
So how do we solve this problem? The solution to this memory occupation is to establish a public sing method, which can be called directly when an object needs it.
Sounds hard? But the truth is very simple. The methods shared by objects are initially put into the constructor. The constructor is a template. All objects generated through this template have almost the same size of memory space. The data attributes of each object are different, which cannot be extracted, but because their common method attributes are consistent, we extract them to greatly reduce the memory space occupied by each object. In JS, a memory space is provided for each constructor to store this common attribute (method), and this space is named as the prototype object. All our common attributes can be thrown into the prototype object space and exist as the attributes of the prototype object.
We use this principle to optimize this case.
So how do you get the prototype object space of a constructor—— Function name.prototype
function Singer(name,gender,age) { this.name = name; this.gender = gender; this.age = age; } Singer.prototype.sing = function(){ console.log('I can sing anything!'); } // Singer.prototype gets the prototype object and adds a property called sing to the prototype object. The value of this property is function() {} // Prototype -- Translation -- prototype object
So how do we call this method for the instantiated object? Very simple, when the prototype object does not exist.
var singer1 = new Singer('Deng Ziqi','famale',28); var singer2 = new Singer('tengger ','male',50); console.log(singer1); console.log(singer2); singer1.sing(); // I can sing anything! singer2.sing(); // I can sing anything! // First, find sing() in singer1. If you can't find it, you'll find it in the prototype object.
We see as like as two peas in the constructor, the result of such a call is exactly the same as the result of the call.
It should be noted that there is not only one prototype object, but a constructor has a prototype object space. Some people may have doubts. In this way, it is still a waste of memory. If I can never use the prototype object space, does it always exist and do not waste space?
Don't fall into misunderstanding.
Suppose a constructor instantiates 10000 objects and 10000 objects have 10000 methods. At this time, we convert 10000 methods into one. Although sometimes we do not necessarily use prototype objects, it is necessary, especially when building large engineering projects.
When an object calls a method, first look in its own memory space. Can't you find it? Well, go directly to the prototype object space that instantiates the constructor of this object. emmm, a bit like his mother gave birth to him. Every time he asked for money, his mother would say: looking for your father?
2, Prototype object
We say that any function Object will appear with a prototype Object when it is defined, but all objects are not generated out of thin air, including the prototype Object. In fact, the prototype Object is the instance Object of Object by default, that is, the construction method of the prototype Object is Object.
Verify:
console.log(typeof Object); // function
Let's go through the process:
Constructor, which is a function. When you create this function, the JS underlying default Object will instantiate a prototype Object for your function. Moreover, there will be a specific attribute prototype in this function, which contains the address of the prototype Object (pointing to the prototype Object), so we can directly call it the prototype Object. This is the same as the Object addresses installed in singer1 and singer2. We directly call both singer1 and singer2 objects.
In the prototype object, there will be a constructor attribute, which is translated into a constructor. It stores the address of the constructor (pointing to the constructor). In other words, a constructor (function object) and its prototype object point to each other through these two properties, so as to confirm the firm binding relationship.
My personal shallow understanding: constructor, instantiated object and prototype object are the relationship between mother, child and father. Mother must be born. It doesn't matter whether father is born or not. But when a child asks for money, his mother asks him to find his father.
We instantiate an object through the constructor. The instantiated child does not have the attribute prototype. However, in order for it to successfully find the prototype object of the constructor, there will be a__ proto__ Properties. As like as two peas, prorotype is the same as the attribute. It is through this attribute that a child finds his father and asks for money.
From this, we can draw a conclusion. According to the cited example, as long as it is a singer I instantiate, there will be in it__ proto__ This property. And this property will point to the prototype object.
function Singer(name,gender,age) { this.name = name; this.gender = gender; this.age = age; } Singer.prototype.sing = function(){ console.log('I can sing anything!'); } var singer1 = new Singer('Deng Ziqi','famale',28); var singer2 = new Singer('tengger ','male',50); console.log(singer1.__proto__); console.log(singer2.__proto__);
The results are as follows:
Because prototype and__ proto__ Both point to the prototype object. In order to distinguish, we call the prototype pointing to the prototype object explicit prototype object__ proto__ Pointing to a prototype object is called an implicit prototype object. Note that it's just different names, but they all point to the same person. For children, it's called father and for mothers, it's called husband.
The memory diagram of this process is as follows:
Every instantiated object will have an attribute pointing to the prototype object. At this time, the prototype object can be used as the public area of all instantiated objects, and the space of the prototype object is the public space.
3, Prototype chain
Let's start with a cat case:
function BlueCat(name,age,gender){ this.name = name; this.age = age; this.gender = gender; } BlueCat.prototype.run = function(){ console.log('Run fast~'); }
Instantiate two objects:
var cat1 = new BlueCat('tom',2,'male'); console.log(cat1); // BlueCat {name: 'tom', age: 2, gender: 'male'} var cat2 = new BlueCat('jerry',3,'male'); console.log(cat2); // BlueCat {name: 'jerry', age: 3, gender: 'male'} cat1.run(); // Run fast~ cat2.run(); // Run fast~
Now let's think deeply: since the prototype object is constructed by object, the corresponding constructor object must also have its prototype object. (however, we often say that the constructor of object is called object object, because the function is also an object. Pay attention to the difference between deep concepts and names, and don't confuse them.) the prototype object of the constructor of object is Object.prototype. Here is the end. Object is also called the origin of all things, so when we try to find the prototype space on its upper layer, the result is null, which is nothingness. (otherwise, there will be an infinite set of dolls)
console.log(Object) //ƒ object() {[native code]} -- ancestor function console.log(Object.prototype) // Object prototype object space console.log(Object.prototype.__proto__) // Object's prototype object's father - object's prototype object's prototype object - null
Let's rewrite the first cat case:
function BlueCat(name,age,gender){ this.name = name; this.age = age; this.gender = gender; } Object.prototype.run = function(){ console.log('Run fast~'); } var cat1 = new BlueCat('tom',2,'male'); var cat2 = new BlueCat('jerry',3,'male'); cat1.run(); // Run fast~ cat2.run(); // Run fast~
As you can see, the result is output normally. Although the running results are the same, the running process is different.
Analysis process: cat1.run();
- First, find cat1 and find that there is no run in cat1.
- Then find the prototype object (the husband of its mother [constructor]) by__ proto__ After entering the prototype object, it is found that there is still no run.
- Then go up and find its mother's husband, her mother's husband. [several steps are omitted here...] because the prototype Object (its father) is the instantiation Object of Object (its grandmother), it finds the prototype Object (its grandfather) of Object upward.
- Found running, not found for nothing.
Summarize the above process in normal language:
- When an object calls a method (or attribute), it will first look for it from its own object space. If it is found, it will be used directly.
- If not found, then pass your own implicit prototype object__ proto__ Go to the prototype object space to find (the prototype object of your constructor). If you find it, use it.
- If it is not found, it will go to the prototype Object space of its own prototype Object (that is, to the prototype Object of its own constructor). If it is found, it will be used. If it is not found, continue to go up until the prototype Object of Object is found. Use it if you find it, and report an error if you can't find it.
We describe the process of finding attributes of this object as a prototype chain.