The prototype
JavaScript is object-based, and functions are also objects. Every function/class in JavaScript has a Prototype property that defines the common ancestor of the object produced by the constructor. Properties and methods defined on it can be shared by object instances.
By default, all stereotype objects automatically get a property called constructor that refers back to the constructor associated with them.
function Test {}console.log(Test.prototype === Test); // true
Copy the code
Each object has an internal attribute [[Prototype]], also known as an implicit Prototype. This property points to the stereotype of the constructor. There is no standard way to access [[Prototype]] in JS, but Firefox, Safari, and Chrome will expose __proto__ properties on every object.
By default, objects created by Object literals implicitly point to Object.prototype, the equivalent of new Object().
In the Chrome console, an object’s implicit prototype constructor shows what type of function/class the object is of.
Prototype chain
When a property is accessed through an object, a search is initiated by the name of the property. The search begins with the object instance itself. If a given name is found on this instance, the value of that name is returned. If the property is not found, the search follows the pointer into the prototype object, and when the property is found on the prototype object, the corresponding value is returned.
Object inheritance is implemented through the [[Prototype]] property to form a Prototype chain.
- For JavaScriptMost of theObject, which eventually inherits from
Object.prototype
Object.prototype.__proto__ == null
And,Cannot be changed- All functions/classes inherit from
Function.prototype
So all of the functions areFunction
type
JavaScript default inheritance:
ES5 implements inheritance
Embezzled constructors
The parent constructor is called in the subclass constructor, and the apply and Call methods are used to execute the constructor in the context of the newly created object.
function SuperType() {
this.colors = 'red';
}
function SubType() {
SuperType.call(this); / / inherit the SuperType
}
Copy the code
Advantages: One advantage of the stolen constructor is that you can pass arguments to the superclass constructor in the subclass constructor
Disadvantages: Because there is no stereotype chain, methods must be defined in constructors to inherit methods, and subclasses cannot be shared, making function reuse impossible
Combination of inheritance
Combinatorial inheritance combines the prototype chain with the embeded constructor to realize the inheritance of attributes and the inheritance of methods through the prototype chain.
function SuperType(name){
this.name = name;
this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name, age){
SuperType.call(this, name); // Inherit attributes
this.age = age;
}
SubType.prototype = new SuperType(); // Inheritance method
Copy the code
Advantages: Compensates for the use of stolen constructors and retains the ability of the instanceof operator and isPrototypeOf() method to recognize synthesized objects
Disadvantages: There are efficiency issues, the main one being that the superclass constructor is always called twice
Primary inheritance
In 2006, Douglas Crockford wrote an article called Prototypal Inheritance in JavaScript. This article introduces an inheritance approach that does not involve constructors strictly. His starting point is that information can be shared between objects through prototypes without custom types. Finally, the paper gives a function:
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
Copy the code
Primitive inheritance applies when you have an object and want to create a new object based on it. You need to pass this object to object() first, and then modify the returned object appropriately.
The new object.create () method in ES5 implements this intent, taking two parameters: the Object as an implicit prototype for the new Object, and the additional properties of the new Object
Parasitic inheritance
Parasitic inheritance is similar to formal inheritance in that you create a function that simply encapsulates the inheritance process, enhances the object internally in some way, and finally returns the object as if it had really done all the work.
function createAnother(original){
var clone = object(original); Create a new object by calling the function
clone.sayHi = function(){ // Enhance the object in some way
console.log("hi");
};
return clone; // Return the object
}
Copy the code
The object() function is not required for parasitic inheritance, and any function that returns a new object can be used here.
Parasitic combinatorial inheritance
Parasitic combinatorial inheritance, that is, inheriting properties by borrowing constructors and inheriting methods through a hybrid form of stereotype chains. The basic idea behind this is that instead of calling the constructor of the supertype to specify the stereotype of the subtype, all you need is a copy of the stereotype of the supertype.
function inherit(subType, superType){
var prototype = object(superType.prototype); // Create an object
prototype.constructor = subType; // Enhance objects
subType.prototype = prototype; // Specify the object
}
Copy the code
The efficiency of parasitic combinatorial inheritance is that it calls the SuperType constructor only once and thus avoids creating unnecessary, redundant properties on subtype. prototype. The advantages of combinatorial inheritance are maintained and the disadvantages of combinatorial inheritance are eliminated.
It is also convenient to use:
function SuperType(name){
this.name = name;
this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
}
function SubType(name, age){
SuperType.call(this, name); // Implement attribute inheritance by borrowing constructors
this.age = age;
}
inherit(SubType, SuperType); // Implement method inheritance through the prototype chain
Copy the code
Parasitic combinational inheritance is also known as the Holy Grail pattern, a favorite implementation:
var inherit = (function () {
var F = function () {};return function (target, origin) {
F.prototype = origin.prototype;
target.prototype = new F();
target.prototype.constructor = target;
target.prototype.uber = origin.prototype; // Make it easy to find the final inherited prototype}; } ());Copy the code
ES6 implements inheritance
Object literal inheritance
Object. GetPrototyeOf was added in ES5 to get the implicit prototype of an Object, but there is no standard way to change the implicit prototype of an instantiated Object.
ES6 has added Object.setPrototypeOf to change the implicit prototype of an Object, using which inheritance can be implemented.
const person = {
greet() {
console.log('hi'); }}const man = {
name: 'man'
}
Object.setPrototypeOf(man, person);
console.log(man);
Copy the code
ES6 references the super keyword, which makes it easier to access object prototypes. You can use super when overriding inherited methods but using the original method.
Differences between ES5 and ES6 practices:
const person = {
getGreeting() {
return 'hi! '; }}const friend1 = {
getGreeting() {
return Object.getPrototypeOf(this).getGreeting.call(this) + 'my friend'; }}const friend2 = {
getGreeting() {
return super.getGreeting() + 'my friend'; }}Object.setPrototypeOf(friend1, person);
console.log(friend1.getGreeting()); // hi! my friend
Object.setPrototypeOf(friend2, person);
console.log(friend2.getGreeting()); // hi! my friend
Copy the code
Super.getgreeting () equals object.getPrototypeof (this).getgreeting. Call (this)
Problems with ES5: When there is multiple inheritance, problems occur due to this.
const person = {
getGreeting() {
return 'hi! '; }}const friend = {
getGreeting() {
return Object.getPrototypeOf(this).getGreeting.call(this) + 'my friend'; }}Object.setPrototypeOf(friend, person);
const obj = Object.create(friend);
obj.getGreeting(); // Uncaught RangeError: Maximum call stack size exceeded
Copy the code
ES6 super can solve the problem well:
const person = {
getGreeting() {
return 'hi! '; }}const friend = {
getGreeting() {
return super.getGreeting() + 'my friend'; }}Object.setPrototypeOf(friend, person);
const obj = Object.create(friend);
console.log(obj.getGreeting()); // hi! my friend
Copy the code
About super
The super reference does not change dynamically; it always points to the correct object.
The concept of a “method” was never formally defined before ES6, and a method is simply an object property with functionality rather than data.
In ES6, a method is formally defined as a function that has an internal [[HomeObject]] property that points to the object that the method belongs to.
All references to super are determined by the [[HomeObject]] property:
- in
[[HomeObject]]
Call on propertyObject.getPrototypeOf
Find the prototype - Search for the prototype function with the same name and bind
this
call
Class inheritance
In ES6, with the class syntax, inheritance can be implemented using the extends keyword, which is technically more complete than parasitic combinative inheritance.
class SuperType {
static sayHi() {
console.log('hi');
}
constructor(name) {
this.name = name;
this.colors = ["red"."blue"."green"];
}
sayName() {
console.log(this.name); }}class SubType extends SuperType {
constructor(name, age) {
super(name);
this.age = age;
}
sayAge() {
console.log(this.age); }}const sub = new SubType('sub'.18);
sub.sayAge(); / / 18
sub.sayName(); // sub
SubType.sayHi(); // hi
Copy the code
Prototype chain after class inheritance:
Reference Books:
- JavaScript Advanced Programming
- Deep Understanding of ES6