1. The prototype chain
Ecma-262 defines the stereotype chain as the main inheritance of ECMAScript. The basic idea is to inherit properties and methods of multiple reference types through stereotypes. Review the relationship between constructors, stereotypes, and instances: each constructor has a stereotype object, the stereotype has a property that points back to the constructor, and the instance has an internal pointer to the stereotype. If the stereotype is an instance of another type that means that the stereotype itself has an internal pointer to another stereotype, which in turn has a pointer to another constructor. This creates a chain of stereotypes between the instance and the stereotype. This is the basic idea of a prototype chain. Implementing the prototype chain involves the following code pattern:
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
};
function SubType() {
this.subproperty = false;
}
/ / inherit the SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function () {
return this.subproperty;
};
let instance = new SubType();
console.log(instance.getSuperValue()); // true
Copy the code
It is easy to see from the above code that the prototype of SubType points to the instantiation object of SuperType so that the instance of SubType not only inherits properties and methods from the instance of SuperType, but also hooks into the prototype of SuperType. The prototype chain diagram in JavaScript is shown below:
The prototype chain implements inheritance characteristics
-
- Instance inheritable properties are: constructor properties of the instance, constructor properties of the superclass, and properties of the superclass prototype.
Problems existing in prototype chain implementation inheritance:
- 1. Reference values contained in the stereotype are shared across all instances
- 2. A child type cannot be instantiated as a parameter to the parent type’s constructor
2. Embezzle constructors
Call the superclass constructor in the subclass constructor. Because functions are, after all, simple objects that execute code in a particular context, you can use the apply() and call() methods to execute constructors in the context of the newly created object. As shown below
function SuperType(name){
this.name = name;
}
function SubType() {
// Inherit SuperType and pass the parameter
SuperType.call(this."Nicholas");
// Instance properties
this.age = 29;
}
let instance = new SubType();
console.log(instance.name); // "Nicholas";
console.log(instance.age); / / 29
Copy the code
Advantages over prototype chain inheritance
- 1. You can pass arguments to the superclass constructor in the subclass constructor
Constructor implementation inheritance problem
- 1. The problem with custom types using constructor patterns: methods must be defined in constructors, so functions cannot be reused
- 2. Subclasses also do not have access to methods defined on the parent class prototype, so all types can only use the constructor pattern. Because of these problems, the stolen constructor is basically not used alone.
3. Combinatorial inheritance
Combinatorial inheritance (sometimes called pseudo-classical inheritance) combines the best of both archetypal chains and stolen constructors. The idea of the base is to use stereotype chains to inherit properties and methods on stereotypes, and to inherit instance properties by stealing constructors. This allows the method to be defined on the prototype for reuse and allows each instance to have its own attributes. See the following example:
function SuperType(name){
this.name = name;
this.colors = ["red"."blue"."green"];
}
SuperType.prototype.sayName = function() {
console.log(this.name);
};
function SubType(name, age){
// Inherit attributes
SuperType.call(this, name);
this.age = age;
}
// Inheritance method
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function() {
console.log(this.age);
};
let instance1 = new SubType("Nicholas".29);
instance1.colors.push("black");
console.log(instance1.colors); // "red,blue,green,black"
instance1.sayName(); // "Nicholas";
instance1.sayAge(); / / 29
let instance2 = new SubType("Greg".27);
console.log(instance2.colors); // "red,blue,green"
instance2.sayName(); // "Greg";
instance2.sayAge(); / / 27
Copy the code
Advantages of combinatorial inheritance
- 1. Combine the advantages of the two modes, including parameter transmission and reuse
Problems with composite inheritance
- 1. If the parent constructor is called twice (memory consumption), the subclass constructor replaces the prototype parent constructor.
4. Original type inheritance
Wrapping an Object with a function and returning a call to that function turns that function into an instance or Object with arbitrary attributes is how object.create () works.
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
Copy the code
The object() function creates a temporary constructor, assigns the passed object to the constructor’s prototype, and returns an instance of the temporary type. In essence, object() makes a shallow copy of the object passed in.
let person = {
name: "Nicholas".friends: ["Shelby"."Court"."Van"]};let anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
let yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
Copy the code
Advantages of original type inheritance
- Old-style inheritance is ideal for situations where you don’t need to create a separate constructor, but you still need to share information between objects.
Deficiencies of original type inheritance
- The reference values contained in the property are always shared between related objects, just as with the stereotype pattern.
Parasitic inheritance
The idea behind parasitic inheritance is similar to the parasitic constructor and factory pattern: Create a function that implements inheritance, enhance an object in some way, and then return that object. The basic parasitic inheritance pattern is as follows:
function createAnother(original){
let 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 this object
}
Copy the code
The advantages of parasitic inheritance
- Instead of creating a custom type, this function automatically becomes the new object being created because it is just a shell that returns the object
The inadequacy of parasitic inheritance
- Adding functions to objects through parasitic inheritance makes them difficult to reuse, similar to the constructor pattern.
Parasitic combinatorial inheritance
Combinatorial inheritance also has efficiency problems. The main efficiency issue is that the superclass constructor is always called twice: once when the subclass stereotype is created and once in the subclass constructor. Parasitic combinatorial inheritance inherits attributes by stealing constructors, but uses a hybrid prototype chain inheritance approach. The basic idea is that instead of assigning a value to a subclass prototype by calling the superclass constructor, you get a copy of the superclass prototype. It boils down to using parasitic inheritance to inherit the parent prototype and then assigning the returned new object to the child prototype. The basic pattern of parasitic combinatorial inheritance is as follows:
function inheritPrototype(subType, superType) {
let prototype = object(superType.prototype); // Create an object
prototype.constructor = subType; // Enhance objects
subType.prototype = prototype; // Assign an object
}
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);
this.age = age;
}
inheritPrototype(SubType, SuperType);
SubType.prototype.sayAge = function() {
console.log(this.age);
};
Copy the code
Characteristics of parasitic inheritance
- The SuperType constructor is called only once, avoiding unnecessary and unnecessary attributes on subtype. prototype, so this example is arguably more efficient. Moreover, the prototype chain remains unchanged, so the instanceof operator and isPrototypeOf() methods work properly. Parasitic combinatorial inheritance is the best model for reference type inheritance.
Class 7.
The previous inheritance approaches have only shown how to emulate class-like behavior using only ECMAScript 5 features. As you can see, each strategy has its own problems and compromises. Because of this, the code that implements inheritance can be very verbose and confusing. To address these issues, ECMAScript 6 introduces the class keyword with the ability to formally define classes. Classes are the new basic syntactic sugar structure in ECMAScript, so you might not be comfortable with them at first. While the ECMAScript 6 class may appear to support formal object-oriented programming, the concepts of stereotypes and constructors are still behind it.
instantiation
Instantiating a class with the new operator is equivalent to calling its constructor with new. The only appreciable difference is that the JavaScript interpreter knows that using new and classes means that instantiation should be done using the constructor function. Calling the class constructor with new does the following.
-
- Create a new object in memory
-
- The [[Prototype]] pointer inside the new object is assigned to the constructor’s Prototype property.
-
- This inside the constructor is assigned to the new object (that is, this refers to the new object).
-
- Executes the code inside the constructor (adding attributes to the new object).
-
- If the constructor returns a non-empty object, that object is returned; Otherwise, the newly created object is returned.
Now that you understand the process of instantiating a class, you can try to implement a new method manually yourself:
// 1
function _new(Obj, ... args) {
let obj = Object.create(Obj.prototype)
let result = Obj.apply(obj, args)
return result instanceof Obj ? result : obj
}
// 2
function _new2() {
// Create an object
let obj = {}
// Get the construction parameters
let Con = [].shift.call(arguments)
// Link to the prototype (the prototype given to the new object obj points to the prototype of its constructor)
obj.__proto__ = Con.prototype
/ / bind this
let result = Con.apply(obj, arguments)
// Make sure new comes out as an object
return result instanceof Con ? result : obj
}
Copy the code