クラス継承は、あるクラスが別のクラスを拡張する方法です。
だから我々は既存の上に新しい機能を作成することができます。
“extends”キーワード
クラスがあるとしましょうAnimal
:
animal
オブジェクトとAnimal
クラスをグラフィカルに表す方法は次のとおりです。
…そして、別のclass Rabbit
を作成したいと思います。,
ウサギは動物であるため、Rabbit
クラスはAnimal
に基づいている必要があり、ウサギは”一般的な”動物ができることを行うことができるように、animalメソッドにアクセスできます。
別のクラスを拡張する構文は次のとおりです。class Child extends Parent
。
class Rabbit
から継承するAnimal
:
Rabbit
クラスのオブジェクトは、Rabbit
rabbit.hide()
などのメソッドにアクセスできます。にAnimal
メソッド、などrabbit.run()
。,
内部的には、extends
キーワードは、古き良きプロトタイプの仕組みを使用して動作します。 これは、Rabbit.prototype.]
をAnimal.prototype
に設定します。 したがって、Rabbit.prototype
にメソッドが見つからない場合、JavaScriptはAnimal.prototype
からメソッドを取得します。,
たとえば、rabbit.run
メソッド、エンジンチェック(画像上のボトムアップ):
我々は章ネイティブプロトタイプから思い出すことができるように、javascript自体は、組み込みのオブジェクトのプロトタイプ継承を使用しています。 たとえば、Date.prototype.]
はObject.prototype
です。 その日のアクセスへの汎用オブジェクト。,
extends
クラス構文では、クラスだけでなく、extends
の後の任意の式を指定できます。
たとえば、親クラスを生成する関数呼び出しは、
function f(phrase) { return class { sayHi() { alert(phrase); } };}class User extends f("Hello") {}new User().sayHi(); // Hello
ここでclass User
f("Hello")
.,
これは、多くの条件に応じてクラスを生成するために関数を使用し、それらから継承できる高度なプログラミングパターンに役立ちます。
メソッドのオーバーライド
では、先に進んでメソッドをオーバーライドしましょう。 デフォルトでは、class Rabbit
で指定されていないすべてのメソッドは、class Animal
から直接”そのまま”取得されます。,
しかし、Rabbit
で独自のメソッドを指定すると、stop()
代わりに使用されます。
class Rabbit extends Animal { stop() { // ...now this will be used for rabbit.stop() // instead of stop() from class Animal }}
通常、親メソッドを完全に置き換えるのではなく、その上にその機能を微調整または拡張する。 私たちはメソッドで何かをしますが、親メソッドの前後またはプロセスで親メソッドを呼び出します。
クラスは"super"
そのためのキーワードを提供します。
super.method(...)
親メソッドを呼び出します。,super(...)
親コンストラクタを呼び出すには(コンストラクタ内のみ)。今Rabbit
親を呼び出すstop
メソッドを持っていますsuper.stop()
プロセス内。コンストラクタをオーバーライドする
コンストラクタを使用すると、少しトリッキーになります。
今まで、
Rabbit
独自のconstructor
を持っていませんでした。,仕様によると、クラスが別のクラスを拡張し、
constructor
を持たない場合、次の”空”constructor
が生成されます:class Rabbit extends Animal { // generated for extending classes without own constructors constructor(...args) { super(...args); }}
ご覧のように、基本的に親
constructor
すべての引数を渡します。 それは、私たち自身のコンストラクタを書かない場合に起こります。次に、カスタムコンストラクタを
Rabbit
に追加しましょう。 それは指定しますearLength
に加えてname
:おっと!, エラーが発生しました。 今はウサギを作ることはできません。 何が悪かったの?
短い答えは次のとおりです。
- 継承クラスのコンストラクタは
super(...)
を呼び出す必要があります。)を使用する前にそれを行いますthis
。
…しかし、なぜですか? ここで何が起こっているの? 確かに、要件は奇妙に思えます。
もちろん、説明があります。 詳細に入ってみましょうので、あなたは本当に何が起こっているのかを理解します。
JavaScriptでは、継承クラスのコンストラクタ関数(いわゆる”派生コンストラクタ”)と他の関数との間に区別があります。, 派生コンストラクターには、特別な内部プロパティ
]:"derived"
があります。 それは特別な内部ラベルです。そのラベルは、
new
の動作に影響します。- 通常の関数が
new
で実行されると、空のオブジェクトを作成し、それをthis
に割り当てます。 - しかし、派生コンストラクタが実行されると、これは行われません。 親コンストラクタがこの仕事をすることを期待します。,したがって、派生コンストラクタは、親(ベース)コンストラクタを実行するために
super
を呼び出さなければなりません。this
のオブジェクトは作成されません。 そして、我々はエラーを取得します。Rabbit
コンストラクタを動作させるには、super()
を使用する前にthis
を呼び出す必要があります。クラスフィールドをオーバーライドする:トリッキーなノート
高度なノートこのノートは、クラスに関するある種の経験があることを前提としています。たぶん他のプログラミング言語で。,
これは、言語に対するより良い洞察を提供し、また、バグの原因となる可能性のある動作を説明します(ただし、あまり頻繁ではありません)。
理解するのが難しい場合は、続けて読み続けてから、しばらくしてから戻ってください。
メソッドだけでなく、クラスフィールドもオーバーライドできます。
がが難しい場合の動作にアクセスしたときに、オーバーライドの分野の親/ルンルン気分/親子のコンストラクタ、さらに他のプログラミング言語まで,ここでは、クラス
Rabbit
拡張Animal
オーバーライドname
フィールドに独自の値を指定します。Rabbit
には独自のコンストラクタはありませんので、Animal
コンストラクタが呼び出されます。興味深いのは、どちらの場合でも、
new Animal()
およびnew Rabbit()
、alert
行の(*)
はanimal
を示します。,その親のコンストラクタは、常に独自のものが使用されてフィールドの値ではなく、オーバーライドします。
それについて奇妙なことは何ですか?
まだ明確でない場合は、メソッドと比較してください。
ここでは同じコードですが、代わりに
this.name
フィールド私たちはthis.showName()
メソッドを呼び出します:注意してください:今、出力が異なっています。
そして、それは私たちが自然に期待するものです。 時の親のコンストラクタは、派生クラスで、オーバーライド法です。
…しかし、クラスフィールドの場合はそうではありません。 を示したことを強調しましたが、親会社のコンストラクタは、必ず親ます。,
なぜ違いがあるのですか?
まあ、理由はフィールドの初期化順序です。 クラスフィールドは初期化されます:
- 基本クラスのコンストラクタの前(何も拡張しない)、
- 派生クラスの直後
super()
。この場合、Rabbit
は派生クラスです。 そこにはconstructor()
はありません。 前述したように、これはsuper(...args)
のみを持つ空のコンストラクタがあった場合と同じです。,だから、
new Rabbit()
呼び出しsuper()
、したがって、親コンストラクタを実行し、(派生クラスのルールに従って)そのクラスフィールドが初期化され 親コンストラクタの実行時には、Rabbit
クラスフィールドはまだありません。Animal
フィールドが使用されます。フィールドとメソッドのこの微妙な違いは、JavaScriptに固有のものです
幸いなことに、この動作は、オーバーライドされたフィールドが親コンストラクタで使用され, その後、何が起こっているのか理解するのは難しいかもしれないので、ここで説明しています。
問題になった場合は、フィールドの代わりにメソッドまたはgetter/setterを使用して修正できます。
Super:internals,]
高度な情報チュートリアルを初めて読んでいる場合は、このセクションはスキップされることがあります。
継承と
super
の背後にある内部メカニズムについてです。のフードの下に少し深く取得してみましょう
super
。 まだ見つかの興味深いものです。,まず、これまで学んできたことから、
super
がまったく機能することは不可能です!ええ、確かに、それは技術的にどのように動作する必要がありますか、自分自身に尋ねてみましょうか? オブジェクトメソッドが実行されると、現在のオブジェクトが
this
として取得されます。 呼び出す場合super.method()
その後、エンジンは現在のオブジェクトのプロトタイプからmethod
を取得する必要があります。 でもどうやって?タスクは簡単に見えるかもしれませんが、そうではありません。, エンジンは現在のオブジェクト
this
を認識しているため、親method
をthis.__proto__.method
として取得できます。 残念ながら、そのような”素朴な”解決策はうまくいきません。問題を実証してみましょう。 クラスがなければ、単純なオブジェクトを使用して簡単にします。
詳細を知りたくない場合は、この部分をスキップして、
]
サブセクションに移動することができます。 それは害にならない または、物事を深く理解することに興味がある場合は読んでください。以下の例では、
rabbit.__proto__ = animal
。, 次に試してみましょう:rabbit.eat()
でanimal.eat()
を呼び出し、this.__proto__
:行で
(*)
eat
プロトタイプからanimal.eat()
を使用します(animal
)そして、現在のオブジェクトのコンテキストでそれを呼び出します。 単純な.call(this)
がここで重要であることに注意してくださいthis.__proto__.eat()
親eat
現在のオブジェクトではなく、プロトタイプのコンテキストで実行されるためです。,上記のコードでは、実際には意図したとおりに動作します:正しい
alert
があります。次に、チェーンにもう一つのオブジェクトを追加しましょう。 私たちは物事がどのように壊れるかを見ていきます:
コードはもう動作しません!
longEar.eat()
を呼び出そうとしているエラーが表示されます。それはそれほど明白ではないかもしれませんが、
longEar.eat()
呼び出しをトレースすると、その理由がわかります。 両方の行で(*)
と(**)
の値this
現在のオブジェクトです(longEar
)。, すべてのオブジェクトメソッドは現在のオブジェクトをthis
として取得し、プロトタイプなどではありません。何が起こるかの画像は次のとおりです。
問題は
this
単独で使用して解決しました。]
ソリューションを提供するために、JavaScriptは関数のためのもう一つの特別な内部プロパティを追加します:
]
。,関数がクラスまたはオブジェクトメソッドとして指定されている場合、その
]
プロパティはそのオブジェクトになります。Then
super
親プロトタイプとそのメソッドを解決するためにそれを使用します。最初にプレーンなオブジェクトでどのように動作するかを見てみましょう:
メソッドは”無料”ではありません
以前に知っていたように、一般に関 したがって、オブジェクト間でコピーし、別の
this
で呼び出すことができます。,]
の存在そのものは、メソッドがオブジェクトを覚えているため、その原則に違反します。]
変更することはできませんので、この結合は永遠です。言語で
]
が使用される唯一の場所は、super
です。 したがって、メソッドがsuper
を使用していない場合でも、オブジェクト間でフリーでコピーできると考えることができます。 しかし、super
物事がうまくいかないかもしれません。,これが間違った
super
コピー後の結果のデモです。tree.sayHi()
“私は動物です”を示しています。 確かに間違ってる理由は簡単です:
何が起こるかの図は次のとおりです。
メソッド、関数プロパティではありません
違いは私たちにとって重要ではないかもしれませんが、javascriptにとって重要です。
以下の例では、比較に非メソッド構文が使用されています。,
]
プロパティが設定されておらず、継承が機能しません。Summary
- クラスを拡張するには:
class Child extends Parent
:- つまり、
Child.prototype.__proto__
はParent.prototype
になるため、メソッドは継承されます。
- つまり、
- コンストラクタをオーバーライドするとき:
- 親コンストラクタを
super()
Child
コンストラクタでthis
を使用する前にthis
として呼び出す必要があります。,別のメソッドをオーバーライドするとき:super.method()
Child
メソッドでParent
メソッドを呼び出すことができます。
- 内部:
- メソッドは、内部の
]
プロパティにクラス/オブジェクトを記憶しています。 これがsuper
親メソッドを解決する方法です。したがって、super
メソッドをあるオブジェクトから別のオブジェクトにコピーすることは安全ではありません。,
- メソッドは、内部の
- 親コンストラクタを
また、
- 矢印関数には独自の
this
またはsuper
がないため、周囲のコンテキストに透過的に収まります。
- クラスを拡張するには:
- 継承クラスのコンストラクタは
© 2021 Tombouctou
Theme by Anders Noren — Up ↑
コメントを残す