8.3 succession
8.3.1 prototype chain
Many object-oriented languages support two kinds of inheritance: interface inheritance and implementation inheritance. The former only inherits the method signature, while the latter inherits the actual method. Interface inheritance is not possible in ECMAScript because the function is not signed. Implementation inheritance is the only inheritance method supported by ECMAScript, which is mainly realized through prototype chain.
// Inherit SuperType SubType.prototype = new SuperType();
SubType. If prototype is an instance of SuperType, its prototype (i.e. SubType.prototype. _proto_) Points to SuperType prototype.
Create its own instance of SubType by assigning a value to its SubType Prototype implements the inheritance of SuperType.
The key to inheritance is that the SubType does not use the default prototype, but replaces it with a new object. This new object happens to be an instance of SuperType.
1 default prototype
By default, all reference types inherit from Object (function), which is also implemented through the prototype chain. The default prototype of any function is an instance of Object, which means that the instance has an internal pointer to Object prototype. As shown in the figure,
2 Relationship between prototype and inheritance
The relationship between prototype and instance can be determined in two ways.
-
instanceof
console.log(instance instanceof Object); // true console.log(instance instanceof SuperType); // true console.log(instance instanceof SubType); // true
-
isPrototypeOf
console.log(Object.prototype.isPrototypeOf(instance)); // true console.log(SuperType.prototype.isPrototypeOf(instance)); // true console.log(SubType.prototype.isPrototypeOf(instance)); // true
3 about methods
4 problems of prototype chain
- When the prototype contains a reference value, it is similar to creating a SubType prototype. colors property, which is shared by all instances of SubType.
- A subtype cannot pass arguments to the constructor of a parent type when instantiated. Because the prototype of the subtype has been constructed.
8.3.2 stealing constructor steaming
Sometimes called "object camouflage" or "classic inheritance". The basic idea is simple: call the parent class constructor in the subclass constructor.
function SuperType() { this.colors = ["red", "blue", "green"]; } function SubType() { // Inherit SuperType SuperType.call(this); } let instance1 = new SubType(); instance1.colors.push("black"); console.log(instance1.colors); // "red,blue,green,black" let instance2 = new SubType(); console.log(instance2.colors); // "red,blue,green"
1 transfer parameters
One advantage of stealing constructors over using prototype chains is that you can pass parameters to the parent constructor in the child constructor.
function SuperType(name){ this.name = name; } function SubType() { // Inherit SuperType and pass parameters SuperType.call(this, "Nicholas"); // Instance properties this.age = 29; } let instance = new SubType(); console.log(instance.name); // "Nicholas"; console.log(instance.age); // 29
2. The problem of stealing constructors
The main disadvantage of stealing constructors is also the problem of using constructor mode to customize types:
- Methods must be defined in constructors, so functions cannot be reused
- Subclasses also cannot access methods defined on the parent class prototype, so all types can only use the constructor pattern
8.3.3 combination inheritance
Combinatorial inheritance combines the prototype chain and stealing constructor, and centralizes their advantages. The basic idea is to use the prototype chain to inherit the properties and methods on the prototype, and inherit the instance properties by stealing the constructor. In this way, the method can be defined on the prototype to realize reuse, and each instance can have its own properties.
function SuperType(name){ this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { console.log(this.name); }; function SubType(name, age){ // Inheritance properties 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
Composite inheritance makes up for the shortcomings of prototype chain and stealing constructor. It is the most used inheritance mode in JavaScript. Moreover, composite inheritance also retains the ability of the instanceof operator and isProtptypeOf() method to identify composite objects.
8.3.4 prototype inheritance
This article introduces an inheritance method that does not involve constructors in the strict sense. His starting point is that information sharing between objects can be realized through prototypes even without custom types.
function object(o) { function F() {} F.prototype = o; return new F(); }
ECMAScript 5 by adding object The create () method normalizes the concept of prototype inheritance. This method takes two parameters: the object that is the prototype of the new object and the object that defines additional properties for the new object (the second optional). When there is only one parameter, object Create () has the same effect as the object() method here:
let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = Object.create(person); anotherPerson.name = "Greg"; anotherPerson.friends.push("Rob"); let yetAnotherPerson = Object.create(person); yetAnotherPerson.name = "Linda"; yetAnotherPerson.friends.push("Barbie"); console.log(person.friends); // "Shelby,Court,Van,Rob,Barbie"
Object. The second parameter of create () is the same as object The second parameter of defineproperties () is the same: each new property is described by its own descriptor. Properties added in this way mask properties with the same name on the prototype object. For example:
let person = { name: "Nicholas", friends: ["Shelby", "Court", "Van"] }; let anotherPerson = Object.create(person, { name: { value: "Greg" } }); console.log(anotherPerson.name); // "Greg"
Prototype inheritance is ideal for situations where you do not need to create a separate constructor, but still need to share information between objects. Remember, however, that the reference values contained in the properties are always shared among related objects, just like using the prototype pattern.
8.3.5 parasitic inheritance
The idea behind parasitic inheritance is similar to parasitic constructor and factory pattern: create a function that implements inheritance, enhance the object in some way, and then return the object.
function createAnother(original){ let clone = object(original); // Create a new object by calling the function clone.sayHi = function() { // Enhance this object in some way console.log("hi"); }; return clone; // Return this object }
Note that adding functions to objects through parasitic inheritance makes functions difficult to reuse, similar to the constructor pattern.
Composite inheritance has efficiency problems. The parent constructor will always be called twice.
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); // The second call to SuperType() this.age = age; } SubType.prototype = new SuperType(); // SuperType() called for the first time SubType.prototype.constructor = SubType; SubType.prototype.sayAge = function() { console.log(this.age); };
8.3.6 parasitic combined inheritance
Parasitic composite inheritance inherits properties by stealing constructors, but uses hybrid prototype chain inheritance methods. The basic idea is to get a copy of the parent class prototype instead of assigning a value to the child class prototype by calling the parent class constructor. The basic mode is as follows:
function inheritPrototype(subType, superType) { let prototype = object(superType.prototype); // create object prototype.constructor = subType; // Enhanced object subType.prototype = prototype; // Assignment 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); };
Parasitic composite inheritance is the best mode of reference type inheritance.