序文
- 以前、オブジェクト指向プログラミングについて記事を書きましたが、JavaScriptの継承方法については他の方のブログを読んで学び、図解を用いて理解を助けました。
- 注意事項は個人的に理解した上で整理したものもありますので、ズレがあるかもしれませんが、読者の皆様もぜひご指摘くださいますよう、よろしくお願いいたします。
免責事項
- 便宜上、この記事で使用されている写真の一部はインターネットから取得したものです。権利侵害がある場合は、ブロガーに連絡して削除してください。写真を提供してくださった他のブロガーに感謝します。
- このメモは、この知識の要約を記録するために使用されます。今後の仕事や勉強をスムーズにするため。
- 侵害がある場合は、すぐに通知し、削除してください。
JavaScript はどのように継承を実装するのでしょうか?
【相続って何?】
-
サブクラスは親クラスの特性と動作を持ちます
- 特徴 == 属性
- 動作 == メソッド
【どうやってするの?】
//父类
function Person(name){
//给构造函数添加了参数
this.name = name;
this.msg = function(){
alert(this.name);
}
}
Person.prototype.age = 10;//给构造函数添加了原型属性
方法 1: プロトタイプ チェーンの継承
ステップ 1: サブクラスを作成し、サブクラスのプロトタイプ オブジェクトが親クラスのインスタンスを指すようにします** (キー ポイント)**
//创建子类
function Sub(){
this.name = 'roy';
}
//子类的原型对象指向父类实例
Sub.prototype = new Person();
ステップ 2: サブクラス インスタンスを作成します。この時点で、サブクラス インスタンスは継承を完了しています。
var sub1 = new Sub();
ステップ 3: 継承が成功したかどうかを確認します (実際には、継承を実現するには上記の 2 つのステップだけで十分です。このステップは、親クラスの呼び出しと、継承が成功したかどうかの確認に属します)
console.log(sub1.age);//10
//instanceof 判断元素是否在另一个元素的原型链上
//sub1继承了Person的属性,返回true
console.log(sub1 instanceof Person);//true
【分析する】
- アドバンテージ
- 実装された継承
- √ インスタンスのコンストラクターの属性 (つまり、サブクラスのコンストラクターの属性)
- √ 親クラスのコンストラクターの属性
- √ 親クラスのプロトタイプの属性
- 実装された継承
- 欠点がある
- 新しいインスタンスは親クラスのコンストラクターにパラメーターを渡すことができません
- 【理由】 ステップ2では、親クラスではなくサブクラスを介して新しいインスタンスが作成されます。
- 単一継承
- 【理由】 図(1)に示すように、サブクラスは親クラスを1つだけ継承します。
- サブクラス インスタンスのプロパティを変更すると、親クラスのプロパティも変更されます。
- 【理由】図(1)のとおり
- 新しいインスタンスは親クラスのコンストラクターにパラメーターを渡すことができません
方法 2: コンストラクターの継承
ステップ 1: サブクラス コンストラクターで親クラス コンストラクターをコールバックする (キー ポイント)
function Sub(){
Person.call(this,'roy');
this.age = 12;
}
ステップ 2: サブクラス インスタンスを作成する
var sub1 = new Sub();
ステップ 3: 継承が成功したかどうかを確認する
console.log(sub1.name);//roy
console.log(sub1.age);//12
console.log(sub1 instanceof Person);//false
【分析する】
- アドバンテージ
- 実装された継承:
- √親クラスのコンストラクターの属性を継承する
- ✕親クラスのプロトタイプのプロパティを継承します(図 (3) を参照)
- 親クラスのコンストラクターにパラメーターを渡すことができます
- 実装された継承:
- 欠点がある
- 親クラスのコンストラクター を再利用できません
- 【理由】一度使用して、再度親クラスのコンストラクタを呼び出します
- 新しいインスタンスにはそれぞれ、親コンストラクターのコピーがあり、肥大化しています。
- 【理由】 サブクラスのコンストラクタ内でクロージャが生成されており、クロージャによるメモリリークが発生しています。
- 親クラスのプロトタイプのプロパティを継承できません
- 【理由】図(3)のとおり
- 親クラスのコンストラクター を再利用できません
方法 3: 結合継承
ステップ 1: サブクラス コンストラクターで親クラス コンストラクターをコールバックする **(キー ポイント)**
function Sub(name){
Person.call(this,name);//构造函数继承
}
ステップ 2: サブクラスを作成し、サブクラスのプロトタイプ オブジェクトが親クラスのインスタンスを指すようにします **(キー ポイント)**
Sub.prototype = new Person();//原型链继承
ステップ 3: サブクラスのインスタンスを作成し、継承が成功したかどうかを確認する
var sub = new Sub('roy');
console.log(sub.name);//roy 继承了构造函数属性
console.log(sub.age);//10 继承了父类原型的属性
【分析する】
- アドバンテージ:
- 実装された継承:
- √ 親クラスのプロトタイプのプロパティを継承する
- パラメータを渡して再利用できます
- 新しいインスタンスごとに導入されるコンストラクター プロパティはプライベートです
- [理由] ステップ 1 でクロージャが生成されました
- 実装された継承:
- 欠点:
- 親クラスのコンストラクターが 2 回呼び出され、メモリを消費します。
- サブクラス コンストラクターは、プロトタイプの親クラス コンストラクターを置き換えます (理解できない場合は、コメント エリアで友達と議論してください)
方法 4: ES6 クラスのクラス継承
ES6形式の親クラスを統一して使用するため、同様に変更を加えます(以前の親クラスをそのまま使用しても問題ありません)
//父类
class Person {
//给构造函数添加
constructor(name) {
this.name = name;
}
msg() {
alert(this.name);
}
}
Person.prototype.age = 10;//给构造函数添加了原型属性
ステップ 1: キーワードを使用してextends
親クラスのメソッドとプロパティを継承します。キーワードはsuper
親クラスのコンストラクターのプロパティを継承します (キーポイント)
class Sub extends Person {
//继承父类方法
constructor(name) {
super(name); //继承父类构造函数属性
this.sex = 'boy';
}
}
ステップ 2: サブクラスのインスタンスを作成し、継承が成功したかどうかを確認する
const sub = new Sub('lao');
console.log(sub.age); //10 //证明继承父类构造函数原型属性
console.log(sub.name); //lao
console.log(sub.sex); //boy
console.log(sub.msg());
console.log(sub instanceof Person); //true
【分析する】
-
アドバンテージ:
- 継承を実装する
- √ 親クラスのプロトタイプのプロパティを継承する
- √親クラスのコンストラクターの属性を継承する
- より簡潔に書かれた
- 継承を実装する
-
欠点:
-
関数宣言は自動的にプロモートできません (使用前に宣言する必要があります)
【例】
let sub1 = new Sub() //报错ReferenceError class Sub{ }
-
方法 5: プロトタイプの継承
ステップ 1: 関数を使用してサブクラス コンストラクターをシミュレートする (キー ポイント)
function content(obj){
function F(){
}; //模拟子类的构造函数
F.prototype = obj; //将子类构造函数原型对象设为父类实例
return new F(); //创建子类构造函数实例
}
ステップ 2: 親クラスのインスタンスを作成する
var person = new Person();
ステップ 3: モック サブクラス コンストラクターを呼び出して、サブクラス インスタンスの作成をシミュレートします。
var sub1 = content(person);
ステップ 4: 継承が成功したかどうかを確認する
console.log(sub1.age);//10 继承了父类函数的属性
【分析する】
- アドバンテージ:
- サブクラスを定義する代わりに、既存のクラスを使用して直接継承します。
- 欠点:
- すべてのインスタンスは、親クラスのプロトタイプのプロパティを継承します (図 (4) を参照)。
- 再利用を実現できません (新しいインスタンス属性は後で追加されます)
PS: Object.create() の原理はプロトタイプの継承によって実現されます。
[略記] 上記のプロトタイプ継承は以下のように略記できます。
var person = new Person(); var sub1 = Object.create(person); console.log(sub1.age);//10 继承了父类函数的属性
方法 6: 寄生遺伝
ステップ 1: 関数を使用してサブクラス コンストラクターをシミュレートする (キー ポイント)
function content(obj){
function F(){
};
F.prototype = obj;//继承了传入的参数
return new F();//返回函数对象
}
ステップ 2: 親クラスのインスタンスを作成する
var person = new Person();
ステップ 3: 継承プロセスをカプセル化するためにのみ使用される関数を作成し、関数の内部は特定の方法で親クラスを継承します。
function subObject(obj){
var sub = content(obj);//通过调用函数
sub.name = 'roy';//以某种方式来增强这个对象
sub.sayHi=function(){
console.log('hello');
}
return sub;//返回子类
}
ステップ 4: 関数を呼び出してサブクラス インスタンスの作成をシミュレートする
var sub1 = subObject(person);
ステップ5:
console.log(typeof subObject);//function
console.log(typeof sub1);//object
console.log(sub1.name);//'roy' 返回了个sub对象,继承了sub的属性
sub1.sayHi(); //'hello'
【分析する】
- アドバンテージ:
- オブジェクトに関数を追加する
- 欠点:
- 効率が低い
- 【理由】 関数の再利用ができない
- 効率が低い
方法 7: 寄生構成の継承
ステップ 1:新しい親クラスを作成し、親クラスにメソッドを追加する
function Parent6() {
this.name = 'parent6';
this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {
return this.name;
}
ステップ 2: サブクラスを作成し、コールバックは親クラスのプロパティを継承します (重要なポイント)
function Child6() {
Parent6.call(this); //继承服父类的属性
this.friends = 'child6';
}
ステップ 3: 関数の助けを借りて、親子クラスの関連付け (キー ポイント)
function clone(parent, child) {
// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程
child.prototype = Object.create(parent.prototype);
child.prototype.constructor = child;
}
clone(Parent6, Child6);
PS: サブクラスのコンストラクターは親クラスのオブジェクトと同等です
【証明する】
console.log(child.prototype.constructor == parent); //true
ステップ 4: サブクラスにメソッドを追加する
Child6.prototype.getFriends = function () {
return this.friends;
}
ステップ 5: 継承を確認する
let person6 = new Child6();
console.log(person6); //输出:带有父类属性的对象>>>>>>证明:实现子类继承父类属性
console.log(person6.getName()); //输出:parent6>>>>证明:实现子类继承父类的方法
console.log(person6.getFriends());//输出:child6>>>证明:子类可以追加方法