Re learn the object-oriented programming of JavaScript (inheritance)

Time:2019-10-18

Re learn the object-oriented programming of JavaScript (inheritance)

1. inheritance

Only implementation inheritance is supported in ES, and its implementation inheritance mainly depends on prototype chain.

2. prototype chain

The concept of prototype chain is described in ES, and prototype chain is used as the main method of inheritance. The basic idea is to use prototypes to let a reference type inherit the properties and methods of another reference type.

Review the relationship between constructors, prototypes, and instances

Each constructor has a prototype object, the prototype object contains a pointer to the constructor, and the instance contains an internal pointer to the prototype object. So if we make the prototype object equal to an instance of another type. At this point, the prototype object will contain a pointer to another prototype, and correspondingly, another prototype will also contain a pointer to another constructor.

In addition, if another prototype is an instance of another type, it will form a chain of instances and prototypes. This is the basic concept of prototype chain.

function SuperType(){
    this.property = true
}

SuperType.prototype.getSuperValue = function(){
    retrun this.property
}

function SubType(){
    this.subproperty = false
}

//Inherited supertype
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function(){
    retrun this.subproperty;
}

var instance = new SubType()
instance.getSuperValue()    // true
Two types are defined in the above code:

Supertype and subtype. Each type has a property and a method. Their main differences are:

Subtype inherits supertype, which is implemented by creating an instance of supertype and assigning the instance to subtype.prototype. The essence of implementation is to rewrite the prototype object and replace it with an instance of a new type.

That is to say, all methods and properties that originally existed in the instance of supertype also exist in subtype.prototype. After adding a method to subtype.prototype, a new method is added on the basis of inheriting the properties and methods of supertype. This implements the instance and the relationship between the constructor and the prototype.

By implementing the prototype chain, the prototype search mechanism is essentially extended. When an instance property is accessed in read mode, the property will be searched in the instance first. If the property is not found, the search continues for the prototype of the instance. In the case of inheritance through prototype chain, the search process can continue up the prototype chain. Until the last step to find the method. When the properties and methods can not be found, the search process always needs to move forward one by one to the end of the prototype chain before it stops.

3. Default prototype

All reference types inherit object by default, and this inheritance is also implemented through prototype chain.The default prototype of all functions is an instance of objectTherefore, the default prototype will contain an internal pointer to object.prototype. This is why all custom types inherit tostring(), valueof() default methods.

4. Relationship between prototype and instance

There are two ways to determine the relationship between a prototype and an instance. The first way is to use the instanceof operator. As long as you use this operator to test the constructors in the instance and prototype chain, the result will return true. As follows:

instance instanceof Object  // true

The second method is isprototypeof(). Similarly, as long as the prototype in the prototype chain appears, it can be said that it is the instance prototype derived from the prototype chain, so isprototypeof() method will also return true

Object.prototype.isPrototypeOf(instance)    // true

5. Define methods carefully

Subtypes sometimes need to override a method in a supertype or add a method that doesn’t exist in a supertype, but anyway,The code that adds a method to the prototype must be placed after the statement that replaces the prototype

function  SuperType() {
    this.property = true
}

SuperType.prototype.getSuperValue = function(){
    retrun this.property
}

function SubType() {
    this.subproperty = false
}
----
//Inherited supertype
SubType.prototype = new SuperType()

//Add new method
SubType.prototype.getSubValue = function(){
    retrun this.subproperty
}
//Overriding methods in supertypes
SubType.prototype.getSuperValue = function(){
    retrun false
}
-----
var instance = new SubType()
instance.getSuperValue()    // false
The separated part of the above code is the definition of the two methods.

The first method getsubvalue() is added to the subtype, and the second method getsupervalue() is a method that already exists in the prototype chain, but overriding this method will mask the original method.

In other words, when the instance of subtype calls getsupervalue(), it will call the redefined method, but when it calls getsupervalue(), it will continue to call the original method. After replacing the prototype with the instance of supertype, you must define the two methods.

Be careful: when inheritance is implemented through a prototype chain, you cannot create a prototype method using object literals because doing so overrides the prototype.

6. Problems of prototype chain

Although the prototype chain is very powerful and can be used to implement inheritance, there are some problems.

1. From the prototype containing the reference type value. As I said before, prototype properties containing reference type values are shared by all instances. So that’s why properties are defined in the constructor, not in the prototype object. When inheritance is implemented through a prototype, the prototype actually becomes an instance of another type. As a result, the original instance attribute has naturally become the current prototype attribute.

2. When creating an instance of a subtype, parameters cannot be passed to the constructor of a supertype. In fact, it can be said that there is no way to pass parameter a to a super type constructor without affecting all object instances.

7. Borrow constructor

Use to call the supertype constructor inside the subtype constructor. That is, the constructor can be executed on the newly created object through the apply () and call () methods.

7.1 transfer parameters

Compared with the prototype chain, borrowing the constructor has a big advantage, that is, you can pass parameters to the supertype constructor in the subtype constructor.

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

function b() {  
    //Inherit s and pass parameters at the same time
    s.call(this, 'nnn')
    //Instance properties
    this.age = 23
}

let i = new B()

i.name // nn
i.age  // 29

7.2 borrowing constructors

If we only borrow the constructor, we can’t avoid the problems of the constructor mode. Methods are defined in the constructor, so function reuse can’t be realized. In addition, the methods defined in the supertype prototype are not visible to subtypes. Result all types can only use constructor mode. So borrowing the constructor pattern is rarely used alone.

8. Combination inheritance

Also known as pseudo classical inheritance, it refers to the combination of prototype chain and borrowed constructor technology. A mode of inheritance that gives full play to their advantages. The principle is to use prototype chain to inherit prototype properties and methods, and use constructor to inherit instance properties.

9. Prototype inheritance

Create a new object with the help of an existing object, first create a temporary constructor, then take the incoming object as the prototype of the constructor, and finally return a new instance of the temporary type.

10. Parasitic inheritance

Create a function that encapsulates the inheritance process only, enhances the object internally in some way, and then returns the object. It also can’t achieve function reuse, which will reduce efficiency.

11. summary

Es supports object-oriented programming, but does not use classes or interfaces. Objects can be created and enhanced during code execution, so they are dynamic rather than strictly defined entities. In the absence of classes, objects can be created in factory mode, constructor mode and prototype mode.

11.1 factory mode

Use simple functions to create an object, add properties and methods to the object, and then return the object. This pattern is replaced by the constructor pattern

11.2 constructor mode

To create a custom reference type, you can use the new operator just as you would create a built-in object instance. However, the constructor pattern also has the disadvantage that each of its members cannot be reused, including functions. Because functions can be not limited to any object. So there is no reason not to share functions among multiple objects.

11.3 prototype mode

Use the prototype property of the constructor to specify which properties and methods should be shared. When combining constructor mode and prototype mode, the constructor is used to define instance properties, and the prototype is used to define shared properties and methods.

JS mainly implements inheritance through prototype chain. The prototype chain is built by assigning an instance of one type to the prototype implementation of another constructor. In this way, subtypes can access all properties and methods of the supertype. This is similar to class based inheritance.

The problem with prototype chains is that object instances share all inherited properties and methods, so they are not suitable for use alone. If you want to solve this problem, you need to rely on the constructor, that is, to call the supertype constructor inside the subtype constructor. In this way, each instance can have its own properties, and at the same time, it can ensure that only the constructor mode is used to define the type.

11.4 prototype inheritance

Inheritance can be implemented without a predefined constructor, which essentially performs a shallow copy of a given object. The replica can be further modified.

11.5 parasitic inheritance

Similar to prototype inheritance, it creates an object based on an object or some information, enhances the object, and finally returns the object. In order to solve the problem of inefficiency caused by multiple calls to the supertype constructor in the composite inheritance pattern, this pattern can be used together with the composite inheritance.

11.6 parasitic combinatorial inheritance

Combining the advantages of parasitic inheritance and combinatorial inheritance is the most effective way to implement type based inheritance.

Pay attention to the public account [Xiaoyao]

公众号小夭同学

Recommended Today

CSS margin overlap and its prevention

The vertical adjacent boundaries of two or more block level boxes coincide. The resulting boundary width is the largest of the adjacent boundary widths. If a negative boundary appears, the negative boundary with the largest absolute value is subtracted from the maximum positive boundary. If there is no positive boundary, the negative boundary with the […]