クラス継承は、あるクラスが別のクラスを拡張する方法です。

だから我々は既存の上に新しい機能を作成することができます。

“extends”キーワード

クラスがあるとしましょうAnimal:

animalオブジェクトとAnimalクラスをグラフィカルに表す方法は次のとおりです。

…そして、別のclass Rabbitを作成したいと思います。,

ウサギは動物であるため、RabbitクラスはAnimalに基づいている必要があり、ウサギは”一般的な”動物ができることを行うことができるように、animalメソッドにアクセスできます。

別のクラスを拡張する構文は次のとおりです。class Child extends Parent

class Rabbitから継承するAnimal:

Rabbitクラスのオブジェクトは、Rabbitrabbit.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 Userf("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を認識しているため、親methodthis.__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は関数のためのもう一つの特別な内部プロパティを追加します:]。,

        関数がクラスまたはオブジェクトメソッドとして指定されている場合、その]プロパティはそのオブジェクトになります。

        Thensuper親プロトタイプとそのメソッドを解決するためにそれを使用します。

        最初にプレーンなオブジェクトでどのように動作するかを見てみましょう:

        メソッドは”無料”ではありません

        以前に知っていたように、一般に関 したがって、オブジェクト間でコピーし、別のthisで呼び出すことができます。,

        ]の存在そのものは、メソッドがオブジェクトを覚えているため、その原則に違反します。 ]変更することはできませんので、この結合は永遠です。

        言語で]が使用される唯一の場所は、superです。 したがって、メソッドがsuperを使用していない場合でも、オブジェクト間でフリーでコピーできると考えることができます。 しかし、super物事がうまくいかないかもしれません。,

        これが間違ったsuperコピー後の結果のデモです。

        tree.sayHi()“私は動物です”を示しています。 確かに間違ってる

        理由は簡単です:

        何が起こるかの図は次のとおりです。

        メソッド、関数プロパティではありません

        違いは私たちにとって重要ではないかもしれませんが、javascriptにとって重要です。

        以下の例では、比較に非メソッド構文が使用されています。, ]プロパティが設定されておらず、継承が機能しません。

        Summary

        1. クラスを拡張するには:class Child extends Parent
          • つまり、Child.prototype.__proto__Parent.prototypeになるため、メソッドは継承されます。
        2. コンストラクタをオーバーライドするとき:
          • 親コンストラクタをsuper()Childコンストラクタでthisを使用する前にthisとして呼び出す必要があります。,別のメソッドをオーバーライドするとき:
            • super.method()ChildメソッドでParentメソッドを呼び出すことができます。
          • 内部:
            • メソッドは、内部の]プロパティにクラス/オブジェクトを記憶しています。 これがsuper親メソッドを解決する方法です。したがって、superメソッドをあるオブジェクトから別のオブジェクトにコピーすることは安全ではありません。,

        また、

        • 矢印関数には独自のthisまたはsuperがないため、周囲のコンテキストに透過的に収まります。

    コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です