この記事では、プロトタイプ チェーンの継承、借用コンストラクターの継承、結合継承、多重継承など、JavaScript の継承に関する知識を提供します。
プロトタイプチェーンの継承
原則
本質プロトタイプ オブジェクトをオーバーライドし、新しい型のインスタンスに置き換えます。次のコードでは、もともと SuperType インスタンス オブジェクトに存在していたプロパティとメソッドが、SubType.prototype にも存在します。
実装
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 は Super を継承しており、継承は Super インスタンスを作成し、そのインスタンスを Sub.prototype に割り当てることで実現されます。 Super のインスタンスにもともと存在していたすべてのプロパティとメソッドは、Sub.prototype にも存在します。写真が示すように。
上の図からわかるように、デフォルトで Sub によって提供されるプロトタイプは使用されず、新しいプロトタイプが与えられます。この新しいプロトタイプはインスタンスです。スーパーの。したがって、新しいプロトタイプは、Super のインスタンスのプロパティとメソッドを持つだけでなく、Super のプロトタイプも指します。最終結果は次のようになります。
ins=>Sub的原型=>Super的原型
getValue() メソッドはまだ Sub.prototype にありますが、value 属性は Sub.prototype にあります。これは、value がインスタンス プロパティであり、getValue() がプロトタイプ メソッドであるためです。 Sub.prototype は Super のインスタンスになっているため、値はそのインスタンス内にあります。
また、元の Sub.prototype のコンストラクターが書き換えられたため、ins.constructor が Super を指すようになっていることに注意してください。
欠点
プライベート プロトタイプ プロパティはインスタンスによって共有されます
サブタイプのインスタンスを作成する場合親型のコンストラクターにパラメーターを渡すことができない場合
プロトタイプ チェーンの継承に関する主な問題: プライベート プロトタイプ プロパティはインスタンスによって共有されるため、これが必要です。プロパティがプロトタイプ オブジェクトで定義されている理由ではなく、プロトタイプを介して継承が実装されると、プロトタイプのインスタンスは別のクラスのインスタンスになります。したがって、元のインスタンス属性が自然に現在のプロトタイプ属性になりました。
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'];
プロトタイプ チェーンの 2 番目の問題は、サブタイプのインスタンスを作成するときに、パラメーターを親タイプのコンストラクターに渡すことができないことです。実際、すべてのインスタンスに影響を与えずに親型のコンストラクターにパラメーターを渡す方法はないと言わなければなりません。参照型の値を含むプロトタイプ プロパティがすべてのインスタンスで共有されるという問題と相まって、実際にはプロトタイプ チェーンの継承が単独で使用されることはほとんどありません
注意事項
使用プロトタイプ チェーンの継承方法では、メソッドの慎重な定義が必要であり、サブタイプでは親クラスのメソッドをオーバーライドしたり、親クラスに存在しないメソッドを追加したりする必要がある場合があります。ただし、何があっても、プロトタイプにメソッドを追加するコードは、プロトタイプを置き換えるステートメントの後に配置する必要があります。
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']
コンストラクターの継承の借用
Principle
コンストラクターの借用 (疑似クラスとも呼ばれます)継承または古典的な継承)。この手法の基本的な考え方は非常に単純で、子クラスのコンストラクター内で親クラスのコンストラクターを呼び出すというものです。関数は特定の環境でコードを実行するオブジェクトにすぎないため、apply() メソッドと call() メソッドを使用して新しく作成されたオブジェクトに対してコンストラクターを実行することもできることを忘れないでください。
実装
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);
パラメータの受け渡し: プロトタイプチェーンと比較して、コンストラクターの継承の借用には大きな利点があります。つまり、サブクラスのコンストラクターの親クラスにパラメーターを渡すことができます。コンストラクターはパラメーターを渡します
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
欠点
コンストラクターを借用しただけでは、コンストラクター パターンの問題を回避することはできません。メソッドはすべてコンストラクターが定義されているため、関数の再利用は問題外です。また、親クラスのプロトタイプで定義されたメソッドはサブクラスからは見えないため、このメソッドが使用されることはほとんどありません。
#結合継承
原則
結合継承とは、プロトタイプ チェーンと継承の借用を指します。コンストラクター テクノロジーを組み合わせて両方の長所を活用するパターン。その背後にある考え方は、プロトタイプ チェーンを使用してプロトタイプのパブリック プロパティとメソッドを継承し、コンストラクターの継承を借用して親クラスのプライベート プロパティを継承することです。このように、関数の再利用は親クラスのプロトタイプでメソッドを定義することによって実現され、各インスタンスは親クラスのプライベート プロパティを持つことが保証されます。 ######成し遂げる###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学习教程】
以上がJavaScript の継承を 10 分で理解しますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。