Inheritance of JS foundation and methods of implementing inheritance

1, What is inheritance?

First, let's understand what prototype chain is:
JavaScript stipulates that all objects have their own prototype. On the one hand, any object can act as the prototype of other objects; On the other hand, because the prototype object is also an object, it also has its own prototype. Therefore, a "prototype chain" will be formed: from the object to the prototype, and then to the prototype of the prototype... (the end of the prototype chain is null)
That is, when accessing the properties of an object, first find it in the basic properties. If not, then follow the_ proto_ This chain looks up. This is the prototype chain.

So, how to judge whether an attribute is basic or found from the prototype? This requires hasOwnProperty():

		function Dog(type) {
            this.type = type;
            this.say = 'wangwang';
        }
		//Add a property to the prototype object
        Dog.prototype.name = 'A mu';
        const dog = new Dog('German Shepherd');
        console.log(dog);
        //It has no name attribute and is inherited
        //hasOwnProperty() determines whether a property or method is its own
        console.log(dog.hasOwnProperty('name')); //false
        console.log(dog.hasOwnProperty('say')); //true

Open the console interface as follows:

You can see that the name attribute is found from the prototype, not its own basic attribute, and the hasOwnProperty attribute is also found from object From prototype.
The prototype chain of objects is along__ proto__ This line goes, so I'm looking for dog When using the hasownproperty property, you will find the object along the prototype chain prototype.
Because the prototype chain of all objects will find object Prototype, so all objects will have object Prototype method. This is called "inheritance".

2, Method of implementing inheritance

1. Prototype inheritance

Instantiate the parent class as the prototype of the child class, so that the child class can access the constructor of the parent class and the properties or methods on the prototype.
The advantage is that it is simple and easy to implement, and the new instances and attribute subclasses of the parent class can be accessed;
The disadvantage is that when creating instances of subtypes, there is no way to pass parameters to the parent class without affecting all object instances.

		function Person(name , age) {  
            this.name = name ; 
            this.age = age ;
            this.type = 'people' ; 
        }

        Person.prototype.say = function () {  
            console.log('I am a human');
        }

        function YellowPerson(name , age) {  
            this.color = 'yellow' ;
            this.talk = function () {  
                console.log('I'm yellow');
            }
        }
        YellowPerson.prototype = new Person('cc' , 18) ;

        const y = new YellowPerson('yy' , 6) ;
        console.log(y);

        console.log(y.type);
        y.say()


You can see that the parameters of the subclass const y = new yellowperson ('yy ', 6) are not passed.

2. Constructor inheritance

The principle is to call the constructor of the parent class and then change its this point, that is, copy the instance attribute of the parent class to the child class
The advantage of this is that it solves the problem of passing parameters from the subclass constructor to the parent class, realizes multiple inheritance (call or apply multiple parent classes), and solves the problem that the instance reference type value contained in the prototype is shared by all instances;
The disadvantage is that you cannot inherit the prototype properties and methods, but only the instance properties and methods of the parent class. The methods are defined in the constructor and cannot be reused;

		function Person(name, age) {
            this.name = name;
            this.age = age;
            this.type = 'people';
        }

        Person.prototype.say = function() {
            console.log('I am a human');
        }


        function YellowPerson(name, age) {
            // Called a function and changed its this point
            Person.call(this, name, age);

            this.color = 'yellow';

            this.talk = function() {
                console.log('I'm yellow');
            }
        }


        const y = new YellowPerson('cc', 18);

        console.log(y);


As you can see, const y = new YellowPerson('cc', 18); The parameters were uploaded successfully, but the say method in the prototype was not inherited successfully.
Constructor solves the problem that reference types are shared by all instances, but it is precisely because of this problem that a very contradictory problem arises - functions are also reference types and can not be shared. In other words, although the functions in each instance are the same, they are not a function, which is equivalent to copying the function code every time we instantiate a subclass.

3. Combinatorial inheritance

It can be seen from the above that the advantages and disadvantages of constructor inheritance and prototype chain inheritance are complementary. Combined inheritance takes the advantages of the above two kinds of inheritance respectively. Constructor inheritance is used for common attributes and prototype chain inheritance is used for functions.

The disadvantage of this is that because the parent class is called twice, two instances are generated, resulting in redundant attributes on the prototype chain.

function Person(name , age) {  
            this.name = name ; 
            this.age = age ;
            this.type = 'people' ; 
        }

        Person.prototype.say = function () {  
            console.log('I am a human');
        }



        function YellowPerson(name , age) {  
            Person.call(this , name , age) ;
            this.color = 'yellow' ;
            this.talk = function () {  
                console.log('I'm yellow');
            }
        }

        YellowPerson.prototype = new Person('cc' , 18) ;


        const y = new YellowPerson('yy' , 6) ;
        console.log(y);

        console.log(y.type);
        y.say()

        console.log(y.name);


You can see that the subclass parameters are uploaded successfully, and the methods in the prototype object are inherited successfully, but redundant parent class properties are also copied, and the parent class constructor is called twice. At the same time, the name attribute will exist on subclass instances and subclass prototype objects. Although according to the prototype chain mechanism, the same name attribute on the prototype object will not be accessed, it is always imperfect.

Optimization of combinatorial inheritance
		function Person(name , age) {  
            this.name = name ; 
            this.age = age ;
            this.type = 'people' ; 
        }

        Person.prototype.say = function () {  
            console.log('I am a human');
        }



        function YellowPerson(name , age) {  
            Person.call(this , name , age)
            this.color = 'yellow' ;
            this.talk = function () {  
                console.log('I'm yellow');
            }
        }

      	//Copy prototype objects deep
        YellowPerson.prototype = {...Person.prototype} ;

        YellowPerson.prototype.aa = 'a' ;


        const y = new YellowPerson('yy' , 19) ;
        console.log(y);


        console.log(Person.prototype);


No redundant attributes, problem solved

4. Parasitic inheritance (middleware inheritance)

Parasitic combinatorial inheritance actually solves the problem of calling the constructor of the parent class twice on the basis of combinatorial inheritance

		function Person(name , age) {  
            this.name = name ; 
            this.age = age ;
            this.type = 'people' ; 
        }

        Person.prototype.say = function () {  
            console.log('I am a human');
        }

        function YellowPerson(name , age) {  
            // Person.call()
            this.color = 'yellow' ;
            this.talk = function () {  
                console.log('I'm yellow');
            }
        }

        
        // Create an empty constructor
        function Fn() {  } 

        Fn.prototype = Person.prototype ;   // Address sharing 

        // In fact, only the prototype object of the parent class is inherited
        YellowPerson.prototype = new Fn() ;

     
        const y = new YellowPerson('yy' , 18) ;
        console.log(y);

Create an empty constructor so that the prototype object of the constructor points to the prototype object of the parent class, that is, the constructor has the properties and methods on the prototype object of the parent class;

It does not inherit the properties and methods of the constructor, but only the prototype methods and prototype properties. This is why composite parasitic inheritance is better than ordinary composite inheritance, because it has been inherited once before, and the attributes and methods of the prototype will not be inherited again.

5. Inheritance of ES6

ES6 provides class syntax sugar and extensions to implement class inheritance. This is also the recommended method in project development.

		class Person {
            // Equivalent to constructor
            constructor(name , age) {
                this.name = name ; 
                this.age = age ; 
                this.type = 'people'
            }
            say() {
                console.log('people');
            }
        }

        // Extensions inheritance
        class YellowPerson extends Person {
            constructor(name , age) {
                //  super inherits the properties and methods of the parent class,
                // This function is similar to person Call (this, name, age) + inheritance of prototype object
                super(name , age) ;  // This sentence must be written when passing parameters to the parent class, otherwise an error will be reported. When super is called as the constructor of the parent class, but this inside super points to the child class
                this.color = 'yellow' ;
            }
           
            talk() {
                console.log('I'm yellow');
            }

        }

        const y = new YellowPerson('yy' , 20) ;
        console.log(y);

Classes in ES6 can be regarded as another way of writing constructors;
The above code shows that the data type of the class is a function, and the class itself points to the constructor;
When using, directly use the new command on the class, which is completely consistent with the usage of the constructor.

Keywords: Javascript Front-end

Added by olanjouw on Fri, 07 Jan 2022 21:18:28 +0200