Home  >  Article  >  Web Front-end  >  Detailed explanation of JS inheritance

Detailed explanation of JS inheritance

php中世界最好的语言
php中世界最好的语言Original
2018-03-13 17:31:301190browse

This time I will bring you a detailed explanation about JS inheritance. What are the precautions when using JS inheritance? Here are practical cases, let’s take a look.

ECMAScript implements inheritance in more than one way. This is because the inheritance mechanism in JavaScript is not explicitly specified, but implemented through imitation. This means that not all inheritance details are entirely handled by the interpreter. You can decide the appropriate inheritance method based on your needs.

Object impersonation

ConstructorUse this keyword to assign values ​​to all properties and methods (that is, using the constructor method of class declaration ). Because the constructor is just a function, you can make the ClassA constructor a method of ClassB and then call it. ClassB will receive the properties and methods defined in the constructor of ClassA.

function ClassA(name) {    this.name = name;    this.sayName = function () {        console.log(this.name);
    };
}function ClassB(name,age) {    this.classA = ClassA;    this.classA(name);    delete this.classA;    this.age = age;    this.sayAge = function(){        console.log(this.age);
    }
}var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);
tom.sayName();                         //'Tom'jerry.sayName();                       //'Jerry'jerry.sayAge();                        //25console.log(tom instanceof ClassA);    //trueconsole.log(jerry instanceof ClassA);  //falseconsole.log(jerry instanceof ClassB);  //true

All new properties and new methods must be defined after deleting the code line of the new method, because the related properties and methods of the super class may be overwritten

Object impersonation can achieve multiple inheritance
If there are ClassA and ClassB, then ClassC wants to inherit these two classes, as follows:

function ClassA(name){    this.name = name;    this.sayName = function (){        console.log(this.name);
    }
}function ClassB(age){    this.age = age;    this.sayAge = function(){        console.log(this.age);
    }
}function ClassC(name,age){    this.method = ClassA;    this.method(name);    
    this.method = ClassB;    this.method(age);    delete this.method;
}var tom = new ClassC('Tom',25);
tom.sayName();                       //'Tom';tom.sayAge();                        //25console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //falseconsole.log(tom instanceof ClassC);  //true

The flaw of this implementation is: if two classes, ClassA and ClassB, have attributes or methods with the same name, ClassB Has high priority because it inherits from later classes.
Due to the popularity of this inheritance method, the third version of ECMAScript added two methods to the Function object, namely call() and apply().

The call method is the method most similar to the classic object impersonation method. Its first parameter is used as the object of this, and other parameters are passed directly to the function itself

function sayName(prefix) {    console.log(prefix + this.name);
};var tom = {};
tom.name = "Tom";
sayName.call(tom, 'This is ');  //'This is Tom'

The function sayName is defined outside the object, but it can also refer to this.
The call method rewrites the object to impersonate

function ClassA(name){    this.name = name;    this.sayName = function(){        console.log(this.name);    }}function ClassB(name,age){    //this.method = ClassA;    //this.method(name);    //delete this.method;    ClassA.call(this,name);    this.age = age;    this.sayAge = function (){        console.log(this.age);    }}var tom = new ClassB('Tom',25);tom.sayName();                       //'Tom'tom.sayAge();                        //25console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //true

The call method replaces the way of using attributes to reference ClassA.

apply

The apply method has two parameters, the object used as this and the parameters to be passed to the function array

function sayName(prefex,mark) {    console.log(prefex+ this.name+ mark);
};var tom = {};
tom.name = 'Tom';
sayName.apply(tom, ['This is ','!']);  //'This is Tom!'

Same You can use apply to rewrite the object to impersonate

function ClassA(name){    this.name = name;    this.sayName = function(){        console.log(this.name);    }}function ClassB(name,age){    ClassA.apply(this,arguments);    this.age = age;    this.sayAge = function (){        console.log(this.age);    }}var tom = new ClassB('Tom',25);tom.sayName();                       //'Tom'tom.sayAge();                        //25  console.log(tom instanceof ClassA);  //falseconsole.log(tom instanceof ClassB);  //true

The parameter array can be passed only when the order of parameters in the super class is completely consistent with the parameters in the subclass

Prototype chain

The prototype object is a template. The objects to be instantiated are based on this template. Any properties and methods of the prototype object are passed to all instances of this class. The prototype chain uses this function to implement the inheritance mechanism.

function ClassA() {}ClassA.prototype.name = 'Tom';ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB() {}ClassB.prototype = new ClassA();var tom = new ClassB();tom.sayName();                       //'Tom'console.log(tom instanceof ClassA);  //trueconsole.log(tom instanceof ClassB);  //true

Here, the prototype attribute of ClassB is set as an instance of ClassA to avoid assigning prototpye attributes one by one.
No parameters are set when calling ClassA, because the constructor must be parameterless in the prototype chain.
In the prototype chain, the result of instanceof has also changed, returning true for both ClassA and ClassB.

Because of the reassignment of prototype attributes, new attributes in the subclass must appear after the prototype is assigned.

function ClassA() {}ClassA.prototype.name = 'Tom';ClassA.prototype.sayName = function () {    console.log(this.name);};function ClassB() {}ClassB.prototype = new ClassA();ClassB.prototype.age = 25;ClassB.prototype.sayAge = function () {    console.log(this.age);};var tom = new ClassA();var jerry = new ClassB();tom.sayName();                         //'Tom'jerry.sayName();                       //'Tom'jerry.name = 'Jerry';tom.sayName();                         //'Tom'jerry.sayName();                       //'Jerry'jerry.sayAge();                        //25console.log(tom instanceof ClassA);    //trueconsole.log(jerry instanceof ClassA);  //trueconsole.log(jerry instanceof ClassB);  //true

The defect of the prototype chain is that multiple inheritance cannot be achieved because the prototype of the class will be rewritten.

Mixed method

The problem with object impersonation is that the constructor method must be used, and the constructor with parameters cannot be used using the prototype chain. However, you can try to combine the two.
Use objects to pretend to inherit the properties of the constructor, and use the prototype chain to inherit the methods of the prototype.

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}
ClassB.prototype = new ClassA();
ClassB.prototype.sayAge = function () {    console.log(this.age);
};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(tom instanceof ClassA);                    //trueconsole.log(jerry instanceof ClassA);                  //trueconsole.log(jerry instanceof ClassB);                  //trueconsole.log(jerry.constructor === ClassA);             //trueconsole.log(ClassB.prototype.constructor === ClassA);  //true

In the ClassB constructor, the object is used to pretend to inherit the name attribute of ClassA, and the prototype chain is used to inherit the sayName method of ClassA. Due to the use of the prototype chain inheritance method, the instanceof running method normal.
But the constructor attribute exposes a problem. Each prototype object has a constructor attribute pointing to its constructor, but the constructor of the ClassB instance points to ClassA, which will cause confusion in the inheritance chain. You can manually modify the constructor's pointer.

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}
ClassB.prototype = new ClassA();
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {    console.log(this.age);
};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(tom instanceof ClassA);                    //trueconsole.log(jerry instanceof ClassA);                  //trueconsole.log(jerry instanceof ClassB);                  //trueconsole.log(ClassA.constructor === ClassB);            //falseconsole.log(jerry.constructor === ClassA);             //falseconsole.log(ClassB.prototype.constructor === ClassA);  //false

Inherit the prototype chain directly

In order to save memory, you can not create a ClassA instance and directly let the prototype of ClassB point to the prototype of ClassA

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}
ClassB.prototype = ClassA.prototype;
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {    console.log(this.age);
};var tom = new ClassA('Tom');var jerry = new ClassB('Jerry',25);console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //trueconsole.log(ClassA.prototype.constructor === ClassB);   //true

This defect is due to the direct Modifying the prototype chain pointer will also affect ClassA's attributes in the ClassB prototype chain, so ClassA has the sayAge method and ClassA's constructor attribute is ClassB.

Empty object as an intermediary

To solve the shortcomings of direct inheritance of the prototype chain, an empty object can be used as an intermediary.

function ClassA(name) {    this.name = name;
}
ClassA.prototype.sayName = function () {    console.log(this.name);
};function ClassB(name, age) {
    ClassA.call(this, name);    this.age = age;
}var fn = function(){};
fn.prototype = ClassA.prototype;
ClassB.prototype = new fn();
ClassB.prototype.constructor = ClassB;
ClassB.prototype.sayAge = function () {    console.log(this.age);
};console.log(ClassA.prototype.hasOwnProperty('sayAge'));  //falseconsole.log(ClassA.prototype.constructor === ClassB);    //false

Although the object instance is still created, since the empty object takes up almost no memory, modifying the prototype of ClassB will not affect ClassA.

Encapsulated into extends method

function extends(child,parent){    var fn = function (){};
    fn.prototype = parent.prototype;
    child.prototype = new fn();
    child.prototype.constructor = child;
    child.super = parent.prototype;
}

The flexibility of JS allows us to implement inheritance in a variety of ways. Understanding the principles and implementation can help us choose the appropriate method in different scenarios.

I believe you have mastered the method after reading the case in this article. For more exciting information, please pay attention to other related articles on the php Chinese website!

Recommended reading:

Use JS code to create barrage effects

Use H5 canvas to create barrage effects

The above is the detailed content of Detailed explanation of JS inheritance. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn