Prototype__proto__

Let’s start with a line of code:

function Parent {}
Copy the code

When we write this simple line of code, two things actually happen

  • A constructor is createdParent
  • A prototype object is createdprototype

The diagram below:

The Parent constructor has a prototype property pointing to the Parent prototype object. The prototype object has a constructor property pointing back to the Parent constructor

Next, we write another line of code:

var parent = new Parent()
Copy the code

At this point, there’s a new member on the picture

Parent: [[prototype]] : __proto__

[[prototype]] is a built-in property of the standard specification, implemented by some browsers with __proto__. For Chrome, this __proto__ does not exist in the parent instance. Object. Prototype is an access descriptor, as evidenced by the following code:

parent.hasOwnProperty('__proto__') // false

Object.prototype.hasOwnProperty('__proto__') // true

Object.getOwnPropertyDescriptor(Object.prototype, '__proto__')
/** * { * configurable: true, * enumerable: false, * get: f __proto__() * set: f __proto__() * } */
Copy the code

The __proto__ access descriptor on Object. Prototype is accessed through the prototype chain.

Six inheritances of ES5

The following is more like a note from JavaScript Advanced Programming, distilling the characteristics and example diagrams of each inheritance.

Prototype chain inheritance

function Parent() {}
function Child() {}

var parent = new Parent()
Child.prototype = parent

var child = new Child()
Copy the code

At this point, based on the details described in Part 1, we can quickly draw what these lines of code do:

This allows the child to access the properties and methods of parent and parent. Prototype by inheritance from the prototype chain. The characteristics of this approach are:

  • Properties of reference types are shared by all instances
  • Cannot pass a value to the parent constructor

Borrowing constructor inheritance (classical inheritance)

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

var child1 = new Child('child1')
var child2 = new Child('child2')
Copy the code

As you can see, this approach has nothing to do with the prototype, so the drawing is pure:

The characteristics of this approach are:

  • Properties on each instance are independent
  • You can pass parameters to the parent constructor
  • The method is created each time an instance is created

Combination of inheritance

As the name implies, it is about the organic combination of the above two inheritance methods. By defining methods in Prototype, attributes are inherited by borrowing constructor inheritance.

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

Parent.prototype.talk = function () {}

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

var parent = new Parent('parent')
Child.prototype = parent
Child.prototype.constructor = Child

var child = new Child('child')
Copy the code

At this point, the diagram has some changes:

As you can see from the figure, instance Child and instance parent each have separate namne but share the talk() method in parent. Prototype. The characteristics of this approach are:

  • There are advantages of both approaches
  • The superclass constructor is executed twiceParent

Primary inheritance

function createObject(o) {
    function F() {}
    F.prototype = o
    return new F()
}

function Parent() {}

var parent = new Parent()

var child = object(parent)
Copy the code

Here we create a createObject function, which is a mock implementation of ES5 Object.create, using the passed Object as a prototype for the created Object.

In contrast to stereotype chain inheritance, we see the same thing except that we don’t have to create a custom constructor Child. So the characteristics are the same as prototype chain inheritance:

  • Properties of reference types are shared by all instances
  • Cannot pass a value to the parent constructor

Parasitic inheritance

Create a function that encapsulates the inheritance process, does the enhancement object internally in some form, and returns the object.

function createObject(o) {
    function F() {}
    F.prototype = o
    return new F()
}

function enhanceObject(o) {
    var clone = createObject(o)
    clone.talk = function() {}
    return clone
}

function Parent() {}

var parent = new Parent()

var child = enhanceObject(parent)
Copy the code

By enhancing objects, each new instance is created with methods that are not shared in Parent. Prototype, but are created separately. Thus, this approach has characteristics similar to borrowing constructor inheritance:

  • Functions can be added to enhance capabilities
  • Methods are created each time an object is created

Parasitic combinatorial inheritance

The biggest drawback we found in composite inheritance is that the parent constructor is called twice, once to set the prototype of the subtype instance:

var parent = new Parent('parent')
Child.prototype = parent
Copy the code

When creating an instance of a subtype:

var child = new Child('child')
Copy the code

Recall the mock implementation of new. In fact, in this sentence, we would execute:

Parent.call(this, name)
Copy the code

So we can see in the example diagram that both parent and child have a name attribute.

Therefore, the createObject method in parasitic combinatorial inheritance indirectly gives child. prototype access to Parent. Prototype, thus reducing the number of calls to the Parent constructor.

function createObject(o) {
    function F() {}
    F.prototype = o
    return new F()
}

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

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

Child.prototype = createObject(Parent.prototype)
Child.prototype.constructor = Child

var child = new Child('child')
Copy the code

An example diagram is as follows:

The efficiency of this approach is that it calls the Parent constructor only once, and thus avoids creating unnecessary, redundant properties on Parent. Prototype. At the same time, the prototype chain stays the same; Therefore, instanceof and isPrototypeOf can also be used normally. Parasitic combinatorial inheritance is generally considered by developers to be the ideal inheritance paradigm for reference types.

Afterword.

Starting with Prototype, there are two parts to describe JavaScript prototypes from two perspectives.

  • Start with Prototype (part 1) – Illustrates ES5 inheritance
  • Start with Prototype — Class and extends in ES6