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 created
Parent
- A prototype object is created
prototype
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 twice
Parent
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