Prototype & Prototype chain

Let’s take a look at an example before we get to the point, such as 👇

function Parent() {}
Copy the code

Try expanding Parent and you’ll see that it has these properties 👇

We then use the Parent constructor to generate an object 🧐

const obj = new Parent()
Copy the code

Very unpretentious code, and we tried to print it out as well

By comparing the printout of the object and function, we can see:

  • The function will have oneprototypeAttribute, whereParent.prototypeThere’s another one attached to itconstructorAnd a__proto__Properties.
  • The object is notprototypeProperty, and you can see it’s just__proto__

In JavaScript Advanced Programming Edition 4, it states:

  • Each time a function is created, one is created for that function according to specific rulesprototypeProperties. By default all prototype objects are automatically given a nameconstructorProperty refers back to the constructor associated with it
  • When you customize the constructor, the stereotype object is only retrieved by defaultconstructorProperty, all other methods inherit fromObject. Instance of each call, inside[[Prototype]]The pointer is assigned to the prototype object of the constructor. There is generally no specified implementation in the standard, but most browsers do it by exposing one__proto__(e.g. Chrome, Safari, Firefox)

Prototype is usually called explicit prototype, __proto__ is called implicit prototype, and the link between prototype and prototype is called prototype chain.

The diagram above shows the relationship between Parent and its prototype, but Parent has a __proto__ attribute. Where should it point?

Function. Prototype: __proto__ refers to Function. Prototype because Parent is generated by Function.

Function.__proto__ refers to function. prototype, but function.prototype. __proto__ refers to object. prototype 😨. But it’s actually quite simple. The types of these attributes tell us the answer 👇

Function.__proto__ refers to Function. Prototype; Function. Function.prototype.__proto__ is an Object whose default prototype is Object.prototype.

At this point, we can draw a complete prototype chain pointing relationship ☺️👇

Complete prototype chain diagram

inheritance

Prototype chain inheritance

Prototype chain inheritance is based on the principle of instance sharing prototype chain, the following is a simple implementation of the code 👇

function Parent() {
    this.name = 'jack'
}

Parent.prototype.getName = function() {
    console.log(this.name)
}

function Child() {}
Child.prototype = Parent.prototype

const child = new Child()
child.getName() / / output jack
Copy the code

As long as the property is mounted on Prototype, instances of subclasses that inherit it can access it. However, inheritance implemented this way has some disadvantages, such as:

  • The stereotype chain is shared by all instances, and the side effects of operations on attributes on the stereotype chain are shared by all instances
  • Unable to initialize a property in a parent class

Constructor inheritance

Constructor inheritance is a way to implement instance attribute sharing between the parent class and the child class by putting the parent class constructor into the child class constructor. The code is 👇

function Parent(name){
    this.name = name
}

function Child(name, age) {
    Parent.call(this, name)
    this.age = age
}

const child = new Child('tony')
Copy the code

The downside of this approach is that you can’t use a stereotype chain to share parent methods (duh), you have to write one in the constructor.

Combination of inheritance

Combinatorial inheritance combines the two methods of inheritance mentioned above 👇

function Parent(name) {
    this.name = name
}

function Child(name, age) {
    Parent.call(this, name)
    this.age = age
}

Parent.prototype.getName = function() {
    console.log(this.name)
}

Child.prototype = new Parent()

const child = new Child('tony'.25)
Copy the code

However, this inheritance implementation method also has its defects, we simply list the shortcomings of this inheritance 👇

  1. The superclass constructor is executed twice
  2. The subclass prototype will be overwritten, resulting inconstructorIt points to the parent class

Prototype inheritance && parasitic inheritance

“JavaScript Advanced Programming 4th Edition” mentioned “primitive inheritance” and “parasitic inheritance” these two inheritance methods, the core code 👇

function create(obj) {
    function F() {}
    F.prototype = obj
    return new F()
}
Copy the code

As you can see, this method of inheritance only links the instance and prototype, and the above mentioned other methods of inheritance is not quite the same, suitable for the need to share object attributes, so it is no wonder that the book is called “inheritance method does not involve strictly constructors” 😀.

Parasitic combinatorial inheritance

“Parasitic combination inheritance”, as the name implies, is a combination of “parasitic inheritance” and “combination inheritance” of the two methods of inheritance. Here’s how to do it 👇

function Parent(name) {
    this.name = name
}

function Child(name, age) {
    Parent.call(this, name)
    this.age = age
}

function initPrototype(obj) {
    function F() {}
    F.prototype = obj
    return new F()
}

Child.prototype = initPrototype(Parent.prototype)
Child.prototype.constructor = Child // Fixed the constructor pointing error caused by copying prototype
Copy the code

As you can see, this implementation does not have the disadvantages of the previous inheritance methods, except that it is “slightly more complex.”

The succession of ES6

With the class keyword provided in ES6, implementing inheritance doesn’t need to be as complicated as it used to be, just through the extends and super keywords. Extends represents inheritance, and super points to the constructor of the parent class.

class Parent {
    constructor(name) {
        this.name = name
    }
}

class Child extends Parent {
    constructor(name, age) {
        super(name)
        this.age = age
    }
}
Copy the code

But in general, ES6 inheritance is just syntactic sugar, and the core is based on prototypes and prototype chains.