This article started on Github, welcome issue/FXXK.

preface

The first version of ES6 was released in June 2015, and this article was originally written in 2016, the early days of my front-end work. At the time, many of ES6’s features were still in their stages and not nearly as ubiquitous as they are now, and I spent a full day understanding prototypes — a huge mountain to climb as a mature JavaScript developer — in order to write JavaScript more easily.

ES6 comes with too much syntactic sugar, where the arrow function obscures the magic of this, and class obscures the archetype of this article at length.

Recently, I rewrote this article, through which you will learn:

  • How to useES5Simulation class;
  • understandprototype__proto__;
  • Understanding prototype chains and prototype inheritance;
  • Learn moreJavaScriptThe language.

Introduction: ordinary objects and function objects

In JavaScript, there’s always a saying that everything is an object. In fact, in JavaScript, objects are different, we can divide them into ordinary objects and function objects. Object and Function are two typical Function objects that come with JavaScript. A function object is a pure function, and a function object is a JavaScript simulation of a class.

So, what exactly is an ordinary object and what is a function object? Take a look at the example below:

First, we create three instances of Function and Object respectively:

function fn1() {}
const fn2 = function() {}
const fn3 = new Function('language'.'console.log(language)')

const ob1 = {}
const ob2 = new Object(a)const ob3 = new fn1()
Copy the code

Print the following result to get:

console.log(typeof Object); // function
console.log(typeof Function); // function
console.log(typeof ob1); // object
console.log(typeof ob2); // object
console.log(typeof ob3); // object
console.log(typeof fn1); // function
console.log(typeof fn2); // function
console.log(typeof fn3); // function
Copy the code

In the above example, ob1, ob2, and ob3 are common objects (all instances of Object), while fn1, fn2, and fn3 are all instances of Function, called Function objects.

How do you tell the difference? Actually, just remember this:

  • allFunctionAre all examples ofThe function object“And all the others areOrdinary objects.

All instances of Function are Function objects. Is Function also an instance of Function?

Save the question for now. Next, to sum up the content of this section:

As you can see from the figure, the implementation of the object itself depends on the constructor. So what exactly is the prototype chain for?

As we all know, as an Object Oriented language, it must have the following characteristics:

  • Object uniqueness
  • abstract
  • inheritance
  • polymorphism

And the biggest purpose of the prototype chain is to achieve inheritance.


Advances: Prototype and __proto__

How exactly does a prototype chain implement inheritance? First, we’ll introduce two brothers: Prototype and __proto__. These are two variables that are ubiquitous in JavaScript (if you debug them often). However, these two variables do not exist on all objects.

Object type prototype __proto__
Common Object (NO)
Function object (FO)

First of all, we draw the following conclusions:

  1. onlyThe function objectwithprototypeThis property;
  2. prototype__proto__Are allJavaScriptCreated automatically when a function or object is definedPredefined attributes.

Next, we verify the above two conclusions:

function fn() {}
console.log(typeof fn.__proto__); // function
console.log(typeof fn.prototype); // object

const ob = {}
console.log(typeof ob.__proto__); // function
console.log(typeof ob.prototype); // undefined, wow! Sure enough, the normal object has no prototype
Copy the code

Since they are preset attributes at the language level, what is the difference between the two? We still proceed from the conclusion and draw the following two conclusions:

  1. prototypeBy example of__proto__Point to (passive)
  2. __proto__Object pointing to the constructorprototype(active)

Wow, that means the following code works:

console.log(fn.__proto__ === Function.prototype); // true
console.log(ob.__proto__ === Object.prototype); // true
Copy the code

If fn is a function object, then what is fn.prototype.__proto__?

Here’s how I tried to solve the problem:

  1. First of all usetypeofgetfn.prototypeThe type of:"object"
  2. Wow, since it is"object"thatfn.prototypeIs it not an instance of Object? Based on the above conclusion, quickly write verification code:
console.log(fn.prototype.__proto__ === Object.prototype) // true
Copy the code

Next, if you were asked to quickly write the JavaScript initialization code for a function prototype when you create it, would you also quickly write:

// The actual code
function fn1() {}

// JavaScript executes automatically
fn1.protptype = {
    constructor: fn1,
    __proto__: Object.prototype
}

fn1.__proto__ = Function.prototype
Copy the code

Here, do you have a sense of enlightenment? In addition, since normal objects are instantiated by function object instantiation (new), and an instance cannot be instantiated again, another object’s __proto__ will not point to its prototype, So the conclusion that normal objects have no Prototype property at the beginning of this section seems fairly straightforward. From the above analysis, we can also see that fn1. protpType is a normal object, it does not have the protpType attribute.

Going back to the last section, we still have a question:

  • Don’tFunctionIs alsoFunctionThe instance?

It’s time to get rid of it and make it true. Now, please show me your code!

Look at the answer
console.log(Function.__proto__ === Function.prototype) // true
Copy the code


Key: Prototype chain

In the last section we looked at Prototype and __proto__ in detail. In fact, these two brothers are mainly there to build prototype chains.

Let’s start with the following code:

const Person = function(name, age) {
    this.name = name
    this.age = age
} / * 1 * /

Person.prototype.getName = function() {
    return this.name
} / * 2 * /

Person.prototype.getAge = function() {
    return this.age
} / * * / 3

const ulivz = new Person('ulivz'.24); / * * / 4

console.log(ulivz) / * * / 5
console.log(ulivz.getName(), ulivz.getAge()) / * * / 6
Copy the code

Explain the implementation details:

  1. perform1, creates a constructorPersonNote that, as mentioned earlier, at this timePerson.prototypeHas been automatically created. It containsconstructor__proto__These two properties;
  2. perform2To objectPerson.prototypeAdded a methodgetName();
  3. perform3To objectPerson.prototypeAdded a methodgetAge();
  4. perform4, by the constructorPersonAn instance is createdulivzIt is worth noting that when a constructor is instantiated, it must automatically execute that constructor.
  5. Get it in the browser5The output of, i.eulivzIt should be:
{
     name: 'ulivz'.age: 24
     __proto__: Object // This is actually 'person.prototype'
}
Copy the code

With the experience of the previous section, the following equation holds:

    console.log(ulivz.__proto__ == Person.prototype)  // true
Copy the code
  1. perform6When, as inulivzCan’t find ingetName()getAge()These two methods, then, continue to look up the prototype chain, which is passed__proto__Look up, then, soon inulviz.__proto__– that is,Person.prototype, stops the search and executes the result.

This is JavaScript archetypal inheritance. To be precise, JavaScript prototype inheritance is implemented through __proto__ with the help of Prototype.

Thus, we can conclude as follows:

  1. Function object__proto__Point to theFunction.prototype; (review)
  2. Function objectprototypePoint to theinstance.__proto__; (review)
  3. Common object__proto__Point to theObject.prototype; (review)
  4. Normal objects do not haveprototypeProperties; (review)
  5. An attempt is made to access a property/method of an object if it is not found on the current objectob.__proto__That is, the prototype of the constructor that accesses the objectobCtr.prototype, if still not found, the search will continueobCtr.prototype.__proto__, like search down in turn. If, at some point, the property is found, the value is returned immediately and the search for the prototype chain stops. If not, the property is returnedundefined.

To test your understanding of the above, analyze the following two questions:

  1. What is the output of the following code?
console.log(ulivz.__proto__ === Function.prototype)
Copy the code
View the results

false


  1. Person.__proto__Person.prototype.__proto__Where do they point to?
View analysis


As mentioned earlier, in JavaScript everything is an object. Person is obviously an instance of Function, so person.__proto__ points to function. prototype:

console.log(Person.__proto__ === Function.prototype)  // true
Copy the code

Because Person.prototype is a normal Object, person.prototype. __proto__ points to Object.prototype

console.log(Person.prototype.__proto__ === Object.prototype)  // true
Copy the code

To verify that there is no Object in the prototype chain of person.__proto__ and that there is no Function in the prototype chain of person.prototype. __proto__, use the following statement:

console.log(Person.__proto__ === Object.prototype) // false
console.log(Person.prototype.__proto__ == Function.prototype) // false
Copy the code


Ultimate: Prototype chain diagram

In the last video, we actually left with a question:

  • If the prototype chain goes on a search, if it can’t be found, when does it stop? In other words, where does the prototype chain end?

We can quickly verify this with the following code:

function Person() {}
const ulivz = new Person()
console.log(ulivz.name) 
Copy the code

Obviously, the above output is undefined. The following is a brief description of the search process:

ulivz                // is an object that can continue
ulivz['name']           // It does not exist
ulivz.__proto__            // is an object that can continue
ulivz.__proto__['name']        // It does not exist
ulivz.__proto__.__proto__          // is an object that can continue
ulivz.__proto__.__proto__['name']     // It does not exist
ulivz.__proto__.__proto__.__proto__       // null !!!! Stop the search and return undefined
Copy the code

Wow, the end of the road is empty.

Finally, go back to the demo code from the previous section:

const Person = function(name, age) {
    this.name = name
    this.age = age
} / * 1 * /

Person.prototype.getName = function() {
    return this.name
} / * 2 * /

Person.prototype.getAge = function() {
    return this.age
} / * * / 3

const ulivz = new Person('ulivz'.24); / * * / 4

console.log(ulivz) / * * / 5
console.log(ulivz.getName(), ulivz.getAge()) / * * / 6
Copy the code

Let’s draw a prototype chain, or let’s draw the whole prototype chain, right? See the picture below:

PS: Hanjian changed CHL (my Chinese initials) to Ulivz (Github’s name), so CHL in this image is actually Ulivz, which I drew when I was using Windows = =

Now that I’ve drawn this picture, almost all of my previous questions have been answered.

Instead of saying that everything is an object, it seems more graphic that everything is empty.


Seasoning: constructor

As mentioned earlier, only the prototype object has the constructor property, which is used to point to the function object that references it.

Person.prototype.constructor === Person //true
console.log(Person.prototype.constructor.prototype.constructor === Person) //true
Copy the code

This is a circular reference. Of course, you can also draw it in the prototype chain diagram in the previous section, so I don’t need to talk about it here.


Added: Archetypal inheritance of the six built-in (function) objects in JavaScript

Through the above discussion, combined with the corresponding code verification, sorted out the following prototype chain diagram:

Thus, we reinforce these two ideas:

  1. Of any built-in function object (class) itself__proto__All point toFunctionThe prototype object of;
  2. In addition toOjectOf the prototype object__proto__Point to thenullFor all other built-in function objects__proto__All point toobject.

In order to reduce the time for readers to type the code, the verification code is given in the hope that it can promote your understanding.

Array:

    console.log(arr.__proto__)
    console.log(arr.__proto__ == Array.prototype)   // true 
    console.log(Array.prototype.__proto__== Object.prototype)  // true 
    console.log(Object.prototype.__proto__== null)  // true 
Copy the code

RegExp:

    var reg = new RegExp;
    console.log(reg.__proto__)
    console.log(reg.__proto__ == RegExp.prototype)  // true 
    console.log(RegExp.prototype.__proto__== Object.prototype)  // true 
Copy the code

Date:

    var date = new Date;
    console.log(date.__proto__)
    console.log(date.__proto__ == Date.prototype)  // true 
    console.log(Date.prototype.__proto__== Object.prototype)  // true 
Copy the code

Boolean:

    var boo = new Boolean;
    console.log(boo.__proto__)
    console.log(boo.__proto__ == Boolean.prototype) // true 
    console.log(Boolean.prototype.__proto__== Object.prototype) // true 
Copy the code

Number:

    var num = new Number;
    console.log(num.__proto__)
    console.log(num.__proto__ == Number.prototype)  // true 
    console.log(Number.prototype.__proto__== Object.prototype)  // true 
Copy the code

String:

    var str = new String;
    console.log(str.__proto__)
    console.log(str.__proto__ == String.prototype)  // true 
    console.log(String.prototype.__proto__== Object.prototype)  // true 
Copy the code


conclusion

A quick summary:

  1. ifAthroughnewTo create theB,B.__proto__ = A.prototype;
  2. __proto__Is the starting point of the prototype chain search;
  3. performB.aIf, in theBCan’t find ina, will be inB.__proto__Student: Middle, which is equal toA.prototypeIfA.prototypeIf you still don’t have it, you’ll keep looking up, and eventually, you’ll find itObject.prototypeIf you can’t find it, becauseObject.prototype.__proto__Point to thenull, so it returnsundefined;
  4. Why is everything empty, or is there, as it were, at the top of the chain of archetypesObject. The prototype. __proto__ - > null.

Finally, I leave you with a question:

  • How to useJavaScriptWhat about the inheritance of the implementation class?

Take a look at the next article in my prototype series, In Depth JavaScript Inheritance.

Above, the end of the text.

Note: In addition, this article is a personal summary, and there may be some omissions in some expressions. If you find any deficiencies in this article, please feel free to point them out in the comments or give me an issue in order to avoid misinterpretation. Thank you ~