I read a lot of relevant articles on the Internet, and many of them are ignorant. It is not to say that you are not good at writing, but that it is not easy to understand in one or two readings. After reading some articles, I organized, summarized and drew some relevant diagrams by myself. I think it will be easier to accept and understand, so I share them here. Therefore, all the understanding and diagrams below are based on personal understanding. If there is any mistake, please forgive me and put forward and correct the mistake in the following part. I am really worried that my immature theoretical foundation will mislead the other brothers.

So let’s start by saying why is this so confusing

Personally, there are three reasons:

  1. JS functions are objects.
  2. Function Object and Object Object are two built-in objects.
  3. Many of the illustrations point to a glance down the gaudy, looking at the headache [manual dog head].

Say again, why are the relevant articles of the predecessors on the Internet difficult to penetrate

Many predecessors start from __proto__ when explaining relevant knowledge points, but in my opinion, __proto__ is closely related to prototype and cannot be mentioned separately (speaking separately means difficult to understand); Prototype has a close relationship with constructor, which creates the awkward situation of having to explain both prototype and the constructor attribute first, which is why these concepts are so difficult for us to understand. (The above personal views are for reference only)

And then I’ll talk about my own personal approach to understanding

In order to understand more easily and with more motivation, I use the method of “deconstructing” step by step from constructor to __proto__ prototype chain to understand, hoping for good results. The article reads as follows:

  1. First understand why functions are objects
  2. Constructor is actually very pure
  3. What is Prototype
  4. Where is the real constructor attribute hidden
  5. __proto__ allows the instance to find its prototype object
  6. What is a prototype chain
  7. Prototype chains lead to new inheritance methods
  8. Learned to use series | a new handwritten
  9. conclusion

Finally, some tips on what you need to know to read on:

function Person() {}
var person1 = new Person()
var person2 = new Person()
Copy the code

The code Person() above is the constructor for person1 and person2.

(2) You can get the constructor that creates the instance object from object. Constructor.

Console. log(person1.constructor) // Result output: [Function: Person]Copy the code

The Person function is the constructor of the Person1 object.

③ Function Function and Object Function are JS built-in objects, also called internal classes, which are wrapped by JS itself. Therefore, there is no need to worry too much about many puzzling and unexpected Settings, such as official actions and fairy operations.

The prototype object is the prototype object in the constructor of the instance object.

1. Understand why “functions are objects”

Let’s start with the following code:

function Person() {... }console.log(Person.constructor) [Function: Function]
// This is the normal function declaration method, which generates the named function. The object model is already generated when the function is declared.
console.log(Function.constructor) [Function: Function]
console.log(Object.constructor) [Function: Function]
Copy the code

The above code constructs a Person function. What information can we see?

  1. Person is declared as a function, but it can also passPerson.constructorOutput content. The Function Function is the constructor of the Person Function.
  2. A Function is also its own constructor.
  3. Functions are also constructors of built-in objects such as Object.

In JS, a Function is an instance object of a Function. That’s what we call a function an object. The above code for declaring functions is almost equivalent to the following code:

// Create a Function object using the Function constructor
var Person = new Function('... ')
/ / almost? This is because the functions generated this way are anonymous, and the object model is generated only when they are actually called.Copy the code

In JS, functions and objects contain the following relation:



Conclusion: Objects are created by functions, which are instances of Function objects.

B) Constructor is pure

Skip __proto__ and prototype and get straight to constructor.

function Person() {}
var person1 = new Person()
var person2 = new Person()
Copy the code

The following diagram shows their constructor direction (ignoring __proto__ and prototype) :



In the figure, the blue background is the instance object of Person, and Person and Function are functions (also objects).

First, we already know that every object can be accessed via object.constructor pointing to the constructor that created the object. Let’s assume that each object has this constructor property and understand as follows:

Note:The constructor property is not necessarily a property of the object itself, but is generalized to a property of the object itself for ease of understanding, so the dashed box is used for the third point of detail.

  1. Person1 and person2 are instances of Person objects whose constructor points to the constructor that created them, the Person function;
  2. Person is a Function, but also a Function instance object, whose constructor points to the constructor that created it, the Function Function;
  3. As for the Function Function, which is a built-in object of JS, we already know from point one that its constructor is itself, so the internal constructor property points to itself.

So the constructor property is nothing more than a property to hold a reference to your own constructor.

All of the following examples will treat a Function object as an instance of the Function object itself, removing its particularity to better understand related concepts.

3. Why did Prototype appear

The previous step was easy to understand, but now you’re asked to add a method to both instances of Person that has the same effect. You write the following code:

// Here is the method sayHello that adds the same effect to person1 and person2 instances
person1.sayHello = function() {
    console.log('Hello! ')
}
person2.sayHello = function() {
    console.log('Hello! ')}console.log(person1.sayHello === person2.sayHello) // false, they are not the same method, each occupies memoryCopy the code

The illustration is as follows:



When you compare these two methods, you will find that they have the same effect, the same name, but are essentially different methods that take up some memory. This is the problem, if there are thousands of instances (hyperbole) to achieve the same effect, then the memory would have to explode. At this point, Prototype appears to solve the problem.

When you need to add methods with the same effect to a large number of instances, you can store them in a Prototype object and place the Prototype object on the constructor of those instances for a shared, common effect. The code is as follows:

Person.prototype.sayHello = function() {
    console.log('Hello! ')}console.log(person1.sayHello === person2.sayHello) // true, same methodCopy the code

The illustration is as follows:



The reason why this form can reduce the waste of memory is that it is no longer necessary to spend part of the memory for the same class instances to simply create attributes or methods related to the same effect, but can directly find and call the constructor’s Prototype object.

Conclusion:The Prototype object holds shared properties and methods of an instance of the same type, essentially for memory purposes.

Function has a prototype object that holds the shared properties and methods of its own instance. Therefore, the above diagram is incomplete and should be changed to the following figure:



SayHello is a function that has its own prototype, but I’ll leave it at that.

Note: create your own prototype inside the constructor.

Where is the real constructor attribute hidden

Constructor attribute () {constructor attribute ();}}

Consider this question:
new Person( )If tens of thousands of resulting instances all have the constructor attribute and all point to creating their own constructors, wouldn’t the third problem arise? They all have the same effect but each takes up a portion of memory?

I’m sure you get the idea; constructor can be stored as a shared property in a prototype object and still point to its own constructor, which is how it is handled. Objects’ constructor properties are placed as shared properties in their prototype objects, as shown below:



Conclusion:The default constructor is actually placed as a shared property in their prototype objects.

One might take a counter example and ask: If it’s a shared property and I change one of the two instances, why isn’t the second instance synchronized? Such as the following code:

function Person() {}
var person1 = new Person()
var person2 = new Person()
console.log(person1.constructor) // [Function: Person]
console.log(person2.constructor) // [Function: Person]
person1.constructor = Function
console.log(person1.constructor) // [Function: Function]
console.log(person2.constructor) / / / the Function: the Person! [Function: Function]Copy the code

This is because person1.constructor = Function does not change the shared constructor property on the prototype object, but adds a constructor property to instance person1. As follows:

console.log(person1) Function {constructor: [Function: Function]}Copy the code

You can see the constructor attribute in the Person1 instance. Its constructor on the prototype object is unchanged.

Well. Huh? Huh? ! Make things? ! ! This makes sense, but the above illustration obviously causes a big problem, as we simply cannot create our own constructor from an object.constructor (no arrow links between them)!

Well, no hurry, the fourth point just shows you why constructor should stay with creating its own constructor, prototype. Next, the __proto__ attribute is exposed.

__proto__ allows the instance to find its prototype object

With the fourth question in mind, if we want to solve this problem, we naturally want to create a property inside the object that points directly to our prototype object. We can find the shared property constructor:

  1. Instance object.__proto__ = Create your own constructor inside the prototype (prototype object)
  2. __proto__. Constructor = Create your own constructor

As shown below:




The __proto__ attribute above is actually set in this way, the object’s __proto__ attribute refers to its prototype object. The Person Function also has a __proto__ attribute pointing to its prototype, which is the prototype of the Function Function. Function has a __proto__ attribute pointing to itself (blue arrow). It uses itself as its constructor, which is a special case that makes no sense.

Here’s the puzzle:Constructor = instance object.__proto__. Constructor?

When an attribute is not found on an instance object, JS looks for the associated shared attribute or method on its prototype object. Therefore, in the above example, the person1 object does not have its own constructor attribute, but its prototype object does. So it can achieve the effect we mentioned above. Of course, there is a chain of prototypes, as long as you know that the above sentence will answer this question for the time being.

Here’s the puzzle:Prototype is an object. It must have a __proto__.

Yes, it is an object, and it does have a __proto__ pointing to its prototype object. Let’s try to find its constructor in code as follows:

function Person() {}
console.log(Person.prototype.__proto__.constructor) // [Function: Object]Copy the code

__proto__ constructor points to the prototype object constructor, so person.prototype.__proto__. Constructor points to the prototype object constructor. The above output shows that prototype’s constructor is Object.

Conclusion:The prototype in this function is just an ordinary Object, and by default is an Object instance.

The following diagram shows all the __proto__ points in the article example, so let’s try to find out what’s wrong with it.



All functions’ __proto__ refers to their prototype object, that is, the Function Function’s prototype object

All functions are instances of Function (including Function itself), so their __proto__ refers to Function’s prototype object.

The last prototype Object is the prototype Object inside the Object function.

As the built-in Object of JS, Object function also plays a very important role. The Object function is the constructor that traces all objects back to the root through the prototype chain. In other words, the official move, the irrational fairy operation.

Prototype __proto__ points to null.

This is because the Object Function is so special that one might wonder, why can’t the Object Function have its __proto__ attribute pointing to its prototype like Function? The answer is that if you point to your prototype, you’re going to go into an infinite loop when you can’t find a property along the prototype chain, so you have to point to null, which is an escape condition.

The above mentioned prototype chain, some little brothers still do not know what it is, then look at what is the prototype chain, understand and then come back to understand the explanation of cat trick three.

What is the prototype chain

Before I tell you what a prototype chain is, let me draw all of the prototype chains in the example above, and see if you can see any patterns. In the example above, there are four prototype chains, and the one connected by the red line is the prototype chain:



The image on the left: a prototype chain is a chain of prototype objects strung together like shish kebabs.

The picture on the right: The Person Function (all functions) is an instance of the Function Function. Assuming it’s a normal instance object, ignoring the Function identity and the prototype object, it’s the same as person1 in the image on the left. Only their __proto__ attribute points to their respective prototype objects.



Since this is a special case where the constructor is itself, the __proto__ property also points to its own prototype object; But its particularity does not affect its prototype Object is still not out of the accident is the Object function instance

The picture on the right: Object is an instance of Function as well as any other Function, so its __proto__ attribute invariably refers to the prototype Object of Function. The __proto__ attribute in the prototype Object of Function refers to the prototype of Object. The __proto__ attribute in Function refers to the prototype of Object. One of the reasons why it is hard to understand is that you have me in you.

To better understand the prototype chain, I’m going to ignore that pesky special case, the Function Function.



Ignore the Function Function and you’ll find it refreshing! As you may have noticed, the __proto__ attribute plays a key role in this. It associates each instance with the prototype object, but since the associated prototype object may also be someone else’s instance object, it forms a chain, which is called the prototype chain.

7. Prototype chain leads to new inheritance methods

Personally, I think the emergence of prototype chain is just a coincidence, not a deliberate existence. But the coincidence does have its own meaning. Remember the two things I said earlier:

  1. The Prototype object holds shared properties and methods that the constructor calls to its instances.
  2. Instance objects that don’t have an attribute use the __proto__ attribute to find the prototype object that created them and look for any associated shared attributes or methods.

That’s where it gets interesting. The Prototype object itself also has a __proto__ attribute pointing to its own prototype object, with shared attributes and methods left behind by the constructor. If you can’t find a shared property or method on your prototype object, then the prototype object will look for its own.



Now imagine if the __proto__ attribute in the prototype Object does not point to null and points to its own prototype? It’s not over. It’s a loop.

Some of you might say, well, isn’t it a constant process of finding values? What’s the point? But because of this coincidence, some lovely people have come up with a new way of inheritance: prototype chain inheritance.

Take a look at the following code:

function GrandFather() {
    this.name = 'GrandFather'
}
function Father() {
    this.age = 32
}
Father.prototype = new GrandFather() // the Father function changes its prototype reference
function Son() {}
Son.prototype = new Father() // Son changes its prototype pointer

var son = new Son()
console.log(son.name) // GrandFather
console.log(son.age)  // Result output: 32
console.log(Son.prototype.constructor) [Function: GrandFather]Copy the code

The correlation pointing diagram is as follows:



Both diagrams omit the Function Function and omit some unnecessary attributes, such as the __proto__ attribute of each major Function.

Left image: Without changing the orientation of each function’s prototype, the default is shown in the left image. Each function’s prototype has their internal __proto__ pointing to the Object function by default (black arrow).

On the right: Father and Son both discard their respective prototype objects and point to a new object. This leads to three interesting new phenomena:

  1. The Father function’s prototype refers to GrandFather’s instance object, which becomes the prototype object for future Father instances. The name private attribute in the GrandFather instance object becomes the shared attribute in the Father instance.
  2. In the same way, the prototype in Son refers to the Father instance, and the private attribute age in Father becomes the shared attribute of the later instance of Son.
  3. Their __proto__ attribute strings them together, forming a new prototype chain.
  1. The constructor direction is unreliable, asSon instance object. ConstructorThe final value is the GrandFather function found along the prototype chain. We knew that Son instances were supposed to be Son functions, but we didn’t expect that.
  2. All inherited properties are shared properties, fatal problem.

So, Emmm, it’s good to know.

Eight, learned to use a new series of | hand

The new keyword creates, in short, an instance of a user-defined object type or of a built-in object with a constructor. And we want to manually implement new keywords, nothing more than to organize a family identification activity, there are two links:

  1. Make an object recognize that its constructor (parent) is that constructor
  2. Make the constructor recognize that the object is an instance of itself.

① Create a Person constructor for example

function Person(identity){
    this.identity = identity || 'Person'
}Copy the code

② If you have a child, create an empty object

var obj = {}
Copy the code

The above statement creates an object literally, which is the same as the following statement

var obj = new Object(a)Copy the code

The empty Object created is an instance of the Object function.

Remember we said above that “empty objects” aren’t really empty inside, they all have a __proto__ attribute that points to their prototype object. The obj Object in the above code is no exception and has a __proto__ attribute pointing to the prototype Object.

We know that when we create an instance of a constructor, the instance should have its __proto__ attribute pointing to the Prototype object in that constructor, so let’s go back to form and let it recognize its parent.

③ Manually point the __proto__ attribute in the instance to the corresponding prototype object.

obj.__proto__ = Person.prototypeCopy the code

The diagram is as follows:



You can see that when the pointer changes, the prototype in the Person function becomes the prototype object of the instance object obj, and the obj.constructor we get naturally becomes the Person function. In other words, OBJ has recognized the Person function as its own constructor, which means we’ve completed the first step of the parental activity.

So the question is, does the Person function recognize this instance?

If the Person function inside is not set as: this identity = identity | | ‘Person’ these statements private property/method (set), it also admits that because be son don’t need other qualifications. Unfortunately, the Person function does have Settings, and these statements are like:

“That’s what you need to be my son: a private property that I set up. But after I own you, whether you change that attribute, whether you want that attribute, I don’t care. “

So now it’s time for the second part:

(4) Call the constructor in the execution environment of the instance and add the private properties/methods set by the constructor.

Person. Apply (obj, arguments) // arguments are argumentsCopy the code

We need to know why constructors are called constructors:

A constructor is a special method used to initialize an object when it is created, that is, to assign initial values to its member variables.


See the key? Assign initial values to object member variables.

Back to the “dad”, the Person function:

function Person(identity){
    this.identity = identity || 'Person' 
}
console.log(Person.identity) // Result output: undefined
// Do not use the name attribute as an example. Each function declaration comes with a name attribute to hold the function name
Copy the code

Confusion: doesn’t this refer to the constructor itself? Why does the Person function have no identity attribute?

The statement inside a function is not executed immediately after it is declared, but only when it is actually called. Therefore, when this is not called, it does not point to it at all, or is not treated as an attribute at all. It is only a code segment, so naturally it will not immediately assign itself an identity attribute. The apply method is used to call the constructor, so that this, which actually exists in the constructor, points to itself and assigns it the corresponding initial property value. Arguments are arguments that can be used to adjust how the initial values are set.

At the end of the process, the instance also has the required properties and methods inside the constructor Person, as shown below:



At this point, we’re done getting the Person constructor to recognize the OBJ object as an instance of itself, and that’s the second step.

⑤ The whole process code is as follows:

// Enter the constructor
function Person(identity){
    this.identity = identity || 'Person'
}
// Enter the instance object
var obj = {}
// Let obJ acknowledge that its constructor is the Person function
obj.__proto__ = Person.prototype
// Link 2: Obj calls Person with the properties/methods that Person sets for the children
// Let the Person function acknowledge that the object is its own instance (child)
Person.apply(obj, ['son'])
// End completes the validation
console.log(obj.constructor) [Function: Person]
console.log(obj.identity) // Output result: sonCopy the code

The above is just an instance object new out of the process, the real implementation of the new method also need to be encapsulated, as follows:

⑥ Encapsulate the new method

// Enter the constructor
function Person(identity){
  this.identity = identity || 'Person'
}
// Encapsulate your new
function _new(Fuc) {
  return function() {
    var obj = {
      __proto__: Fuc.prototype
    }
    Fuc.apply(obj, arguments)
    return obj
  }
}
// The test is as follows
var obj = _new(Person)('son')
console.log(obj.constructor) [Function: Person]
console.log(obj.identity) // Output result: son
Copy the code

Perfect, everyone’s happy! Applause!

Nine,

Recently I learned how to do mind mapping, so I tried to summarize it directly with mind mapping:







After writing this article, I feel a lot clearer, of course, I am not sure whether some of the internal views are correct, most of the views are based on my predecessors’ articles and my own thinking summed up some more reasonable arguments. Thank you for your reading, if there are any serious mistakes, be sure to understand and put forward.

Finally, thanks to a blog post that gave me a basic understanding of these concepts:

Help you understand prototype, __proto__ and constructor thoroughly

You can read that article a few times if you think I’m bad at it.

End