This article brings you knowledge about inheritance in JavaScript, including prototype chain inheritance, borrowed constructor inheritance, combined inheritance and multiple inheritance. I hope it will be helpful to you.
Prototype chain inheritance
Principle
Essence Is to override the prototype object and replace it with an instance of a new type. In the following code, the properties and methods that originally existed in the SuperType instance object now also exist in SubType.prototype.
Implementation
function Super(){ this.value = true; } Super.prototype.getValue = function(){ return this.value } function Sub(){}; // Sub继承了Super Sub.prototype = new Super(); Sub.prototype.constructor = Sub; const ins = new Sub(); console.log(ins.getValue()); // true
Sub inherits Super, and inheritance is achieved by creating a Super instance and assigning the instance to Sub.prototype. All properties and methods that originally existed in the instance of Super now also exist in Sub.prototype. as the picture shows.
As can be seen from the above figure, the prototype provided by Sub by default is not used, but a new prototype is given to it; this new prototype is an instance of Super. Thus, the new prototype not only has the properties and methods of an instance of Super, but it also points to Super's prototype. The end result is this:
ins=>Sub的原型=>Super的原型
getValue() method is still in Sub.prototype, but the value attribute is in Sub.prototype. This is because value is an instance property and getValue() is a prototype method. Since Sub.prototype is now an instance of Super, the value is located in that instance.
In addition, please note that ins.constructor now points to Super. This is because the constructor in the original Sub.prototype has been rewritten.
Disadvantages
Private prototype properties will be shared by instances
When creating instances of subtypes When, you cannot pass parameters to the constructor of the parent type
The main problem with prototype chain inheritance: private prototype properties will be shared by the instance, and this is why it is necessary to , rather than the reason why the properties are defined in the prototype object. When inheritance is implemented through prototypes, the prototype instance becomes an instance of another class. Therefore, the original instance attributes naturally became the current prototype attributes.
function Super(){ this.colors = ['red','green','blue']; } Super.prototype.getValue = function(){ return this.colors } function Sub(){}; //Sub继承了Super Sub.prototype = new Super(); const ins1 = new Super(); ins1.colors.push('black'); console.log(ins1.colors);//['red','green','blue','black']; const ins2 = new Sub(); console.log(ins2.colors);//['red','green','blue','black'];
The second problem with the prototype chain is that when creating an instance of a subtype, parameters cannot be passed to the constructor of the parent type. In fact, it should be said that there is no way to pass parameters to the constructor of the parent type without affecting all instances. Coupled with the problem that prototype properties containing reference type values will be shared by all instances, in practice prototype chain inheritance is rarely used alone
Attention issues
Use The prototype chain inheritance method requires careful definition of methods. Subtypes sometimes need to override a method of the parent class, or add a method that does not exist in the parent class. But no matter what, the code that adds methods to the prototype must be placed after the statement that replaces the prototype.
function Super() { this.colors = ['red', 'green', 'blue']; } Super.prototype.getValue = function() { return this.colors } function Sub() { this.colors = ['black']; }; //Sub继承了Super Sub.prototype = new Super(); //添加父类已存在的方法,会重写父类的方法 Sub.prototype.getValue = function() { return this.colors; } //添加父类不存在的方法 Sub.prototype.getSubValue = function(){ return false; } const ins = new Sub(); //重写父类的方法之后得到的结果 console.log(ins.getValue()); //['black'] //在子类中新定义的方法得到的结果 console.log(ins.getSubValue());//false //父类调用getValue()方法还是原来的值 console.log(new Super().getValue());//['red', 'green', 'blue']
Borrowing constructor inheritance
Principle
Borrowing constructor (sometimes also called Pseudo-class inheritance or classic inheritance). The basic idea of this technique is quite simple, which is to call the parent class constructor inside the child class constructor. Don’t forget that functions are nothing but objects that execute code in a specific environment, so constructors can also be executed on newly created objects by using the apply() and call() methods.
Implementation
function Super() { this.colors = ['red', 'green', 'blue']; } Super.prototype.getValue = function(){ return this.colors; } function Sub(){ //继承了Super Super.call(this);//相当于把构造函数Super中的this替换成了ins实例对象,这样在Super只有定义的私有属性会被继承下来,原型属性中定义的公共方法不会被继承下来 } const ins = new Sub(); console.log(ins.colors);
Passing parameters: Compared with the prototype chain, borrowing constructor inheritance has a great advantage, that is, you can pass parameters to the subclass constructor Parent class constructor passes parameters
function B(name){ this.name = name; } function A(){ //继承了B,同时还传递了参数 B.call(this,'ZZ'); //实例属性 this.age = 100; } const p = new A(); alert(p.name);//'ZZ' alert(p.age);//100
Disadvantages
If you just borrow the constructor, you will not be able to avoid the problems of the constructor pattern - the methods are all in the constructor is defined, so function reuse is out of the question. Moreover, methods defined in the prototype of the parent class are not visible to the subclass, so this method is rarely used.
Combined inheritance
Principle
Combined inheritance refers to the prototype chain and borrowing An inheritance pattern in which constructor technology is combined to take advantage of the best of both worlds. The idea behind it is to use the prototype chain to inherit the public properties and methods on the prototype, and to inherit the private properties of the parent class by borrowing constructor inheritance. In this way, function reuse is achieved by defining methods on the parent class prototype, and each instance is guaranteed to have the private properties of the parent class.
accomplish
function Super(name){ this.name = name; this.colors = ['red','blue','green']; } Super.prototype.sayName = function(){ alert(this.name); } function Sub(name,age){ Super.call(this,name); this.age = age; } // 继承方法 Sub.prototype = new Super(); Sub.prototype.constructor = Sub; Sub.prototype.sayAge = function(){ alert(this.age); } const ins = new Sub('jarvis',18); ins.colors.push('black'); console.log(ins.colors);// ["red", "blue", "green", "black"] ins.sayName();//'jarvis' ins.sayAge();//18 const ins2 = new Sub('ershiyi',21); console.log(ins2.colors);//["red", "blue", "green"] ins2.sayName();//'ershiyi' ins2.sayAge();//21
在上个例子中,Sub构造函数定义了两个属性:name和age。Super的原型定义了一个sayName()方法。在Sub构造函数中调用Super构造函数时传入了name参数,紧接着又定义它自己的属性age。然后,将Super的实例赋值给Sub的原型,然后又在该新原型上定义了方法sayAge()。这样一来,就可以让不同的Sub实例分别拥有自己的属性——包括colors属性,又可以使用相同的方法组合继承避免了原型链和借用构造函数的缺陷,融合了他们的优点,称为JavaScript中最常用的继承模式。
缺点
无论在什么情况下,都会调用两次父类的构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数内部。
寄生组合式继承
原理
组合继承是JavaScript最常用的继承模式;不过,它也有自己的不足。组合继承最大的问题就是无论什么情况下,都会调用两次父类构造函数:一次是在创建子类原型的时候,另一次是在子类构造函数内部。没错,子类型最终会包含超类型对象的全部实例属性,但不得不在调用子类型构造函数时重写这些属性。再来看一看下面组合继承的例子。
实现
function Super(name){ this.name = name; this.colors = ['red','blue','green']; } Super.prototype.sayName = function(){ alert(this.name); } function Sub(name,age){ Super.call(this,name); this.age = age; } // 继承方法 Sub.prototype = new Super(); Sub.prototype.constructor = Sub; Sub.prototype.sayAge = function(){ alert(this.age); } const ins = new Sub('jarvis',18); ins.colors.push('black'); console.log(ins.colors);// ["red", "blue", "green", "black"] ins.sayName();//'jarvis' ins.sayAge();//18 const ins2 = new Sub('ershiyi',21); console.log(ins2.colors);//["red", "blue", "green"] ins2.sayName();//'ershiyi' ins2.sayAge();//21
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背 后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,所需要的无非就是超类型原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。寄生组合式继承的基本模式如下所示。
function Super(name){ this.name = name; this.colors = ['red','blue','green']; } Super.prototype.sayName = function(){ alert(this.name); } function Sub(name,age){ //继承实例属性 Super.call(this,name); this.age = age; } // 继承公有的方法 Sub.prototype = Object.create(Super.prototype); Sub.prototype.constructor = Sub; Sub.prototype.sayAge = function(){ alert(this.age); } const ins = new Sub('jarvis',18); ins.colors.push('black'); console.log(ins.colors);// ["red", "blue", "green", "black"] ins.sayName();//'jarvis' ins.sayAge();//18 const ins2 = new Sub('ershiyi',21); console.log(ins2.colors);//["red", "blue", "green"] ins2.sayName();//'ershiyi' ins2.sayAge();//21
多重继承
JavaScript中不存在多重继承,那也就意味着一个对象不能同时继承多个对象,但是可以通过变通方法来实现。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>18 多重继承</title> </head> <body> <script type="text/javascript"> // 多重继承:一个对象同时继承多个对象 // Person Parent Me function Person(){ this.name = 'Person'; } Person.prototype.sayName = function(){ console.log(this.name); } // 定制Parent function Parent(){ this.age = 30; } Parent.prototype.sayAge = function(){ console.log(this.age); } function Me(){ // 继承Person的属性 Person.call(this); Parent.call(this); } // 继承Person的方法 Me.prototype = Object.create(Person.prototype); // 不能重写原型对象来实现 另一个对象的继承 // Me.prototype = Object.create(Parent.prototype); // Object.assign(targetObj,copyObj) Object.assign(Me.prototype,Parent.prototype); // 指定构造函数 Me.prototype.constructor = Me; const me = new Me(); </script> </body> </html>
ES5 与 ES6 继承差异
在 ES5 的传统继承中, this 的值会先被派生类创建,随后基类构造器才被调用。这意味着 this 一开始就是派生类的实例,之
后才使用了基类的附加属性对其进行了装饰。
在 ES6 基于类的继承中, this 的值会先被基类创建,随后才被派生类的构造 器所修改。结果是 this 初始就拥有作为基类的内置对象的所有功能,并能正确接收与之关联的所有功能。
【相关推荐:javascript学习教程】
The above is the detailed content of Take you to understand JavaScript inheritance in ten minutes. For more information, please follow other related articles on the PHP Chinese website!