JavaScriptのプロトタイプベースのクラス継承のガイド

多くの場合、コンピューター言語は、1つのオブジェクトを継承する方法を提供します
別のオブジェクト。継承されたオブジェクトには、親オブジェクトのすべてのプロパティが含まれます。また、独自の一意のプロパティセットも指定します。

JavaScriptのヒントと本の発表については、Twitterでフォローしてください。

JavaScriptオブジェクトは、プロトタイプベースの継承を使用します。その設計は、厳密にオブジェクト指向プログラミング言語のクラス継承と論理的に似ています(ただし、実装が異なります)。

メソッドまたはプロパティがオブジェクトのプロトタイプにアタッチされると、それらはそのオブジェクトとその子孫で使用できるようになると言って大まかに説明できます。しかし、このプロセスはしばしば舞台裏で行われます。

コードを記述するとき、prototypeプロパティに直接触れる必要さえありません。 splitメソッドを実行するときは、次のように文字列リテラルから直接呼び出します。「hello」.split(「e」)または変数から:string.split(“、”);

クラスを使用し、キーワードを内部で拡張する場合、JavaScriptは引き続きプロトタイプベースの継承を使用します。構文を単純化するだけです。おそらくこれが、プロトタイプベースの継承がどのように機能するかを理解することが重要な理由です。まだ言語設計の中核です。

これが、多くのチュートリアルでString.splitの代わりにString.prototype.splitが表示される理由です。これは、オブジェクトのプロトタイププロパティに関連付けられているため、文字列型のオブジェクトで使用できるメソッド分割があることを意味します。

オブジェクトタイプの論理階層の作成

猫と犬は、動物から継承されたペットから継承されます。

犬と猫は同じような特徴を共有しています。 2つの異なるクラスを作成する代わりに、
1つのクラスPetを作成し、そこからCatとDogを継承できます。ただし、Petクラス自体は、Animalクラスから継承することもできます。

始める前に

プロトタイプを理解しようとすることは、コーディングからコンピューター言語の設計に移行するようなものです。 2つのまったく異なる知識の領域。

技術的には、クラスの軽い知識とキーワードの拡張だけでソフトウェアを作成できます。プロトタイプを理解しようとすることは、言語設計の暗い隅に立ち入るようなものです。そして時々それは洞察力に富むことがあります。

このチュートリアルだけでは十分ではありません。私はあなたが正しい方向に導くことを望んでいるいくつかの重要なことだけに焦点を合わせました。

フードの下

オブジェクト継承の背後にある考え方は、階層の構造を提供することです
同様のオブジェクト。子オブジェクトは、その親から「派生」していると言うこともできます。

JavaScriptでのプロトタイプチェーンの作成方法。

技術的には、これは見た目です。これについて考えすぎないようにしてください。階層の最上部にObjectオブジェクトがあることを知ってください。それが、独自のプロトタイプがnullを指している理由です。その上には何もありません。

プロトタイプベースのオブジェクト継承

JavaScriptは、プロトタイプと呼ばれるものを介したオブジェクトの継承をサポートしています。各オブジェクトには、prototypeというオブジェクトプロパティが付加されています。

クラスを操作してキーワードを拡張するのは簡単ですが、プロトタイプベースの継承がどのように機能するかを実際に理解することは簡単ではありません。願わくば、このチュートリアルが霧の少なくとも一部を取り除くことを願っています!

オブジェクトコンストラクター関数

関数はオブジェクトコンストラクターとして使用できます。通常、コンストラクター関数の名前は大文字で始まり、通常の関数を区別します。オブジェクトコンストラクターは、オブジェクトのインスタンスを作成するために使用されます。

JavaScriptの組み込みオブジェクトの一部は、同じルールに従って既に作成されています。たとえば、Number、Array、およびStringはObjectから継承されます。前述したように、これは、Objectにアタッチされたプロパティがすべての子で自動的に使用可能になることを意味します。

コンストラクター

コンストラクター関数の構造を理解せずにプロトタイプを理解することは不可能です。

それでは、カスタムコンストラクター関数を作成するとどうなりますか?クラス定義には、constructorとprototype.constructorの2つのプロパティが魔法のように表示されます。

それらは同じオブジェクトを指していません。それらを分解しましょう:

新しいクラスCraneを定義するとしましょう(関数またはクラスのキーワードを使用します)。

作成したばかりのカスタムコンストラクターが、カスタムCraneクラスのprototypeプロパティにアタッチされました。独自のコンストラクターを指すリンクです。循環ロジックを作成します。しかし、それはパズルのほんの一部です。

Crane.constructorを見てみましょう。

Crane.constructor自体は、作成元のオブジェクトのタイプを指します。
すべてのオブジェクトコンストラクターはネイティブ関数であるため、Crane.constructorが指すオブジェクトはFunction型のオブジェクト、つまり関数コンストラクターです。

Crane.prototype.constructorとCrane.constructorの間のこの動的な動きは、分子レベルでのプロトタイプの継承を可能にします。 JavaScriptコードを記述するときに、これについて考える必要はほとんどありません。しかし、これは間違いなくインタビューの質問です。

もう一度簡単に見てみましょう。 Crane.prototype.constructorは、独自のコンストラクターを指します。 「私は私です」と言っているようなものです。

classキーワードを使用してクラスを定義すると、まったく同じことが起こります。

ただし、Crane.constructorプロパティはFunctionコンストラクターを指します。

それがリンクの確立方法です。

これで、Craneオブジェクト自体が別のオブジェクトの「プロトタイプ」になります。そして、そのオブジェクトは別のオブジェクトのプロトタイプになることができます。等々。チェーンは永遠に続くことができます。

サイドノート:ES5スタイルの関数の場合、関数自体は
コンストラクタ。ただし、ES6クラスキーワードは、コンストラクターをスコープ内に配置します。これは単なる構文上の違いです。

プロトタイプベースの継承

常にクラスを使用し、キーワードを拡張してオブジェクトを作成および継承する必要があります。しかし、それらは実際に舞台裏で行われていることのキャンディーラッパーにすぎません。

ES5スタイルの構文を使用してオブジェクト継承階層を作成することは古く、プロのソフトウェア開発者の間ではめったに見られませんが、それを理解することで、実際にどのように機能するかについてより深い洞察を得ることができます。

新しいオブジェクトBirdを定義し、タイプ、色、卵の3つのプロパティを追加しましょう。また、フライ、ウォーク、lay_eggの3つのメソッドを追加しましょう。すべての鳥ができること:

lay_eggメソッドを意図的にグレーアウトしていることに注意してください。私たちを覚えています
Bird.prototypeが独自のコンストラクターを指していることを以前に説明しましたか?

また、次の例に示すように、卵の産卵メソッドをBird.prototypeに直接アタッチすることもできます。

一見すると、Bird内でthisキーワードを使用してメソッドをアタッチする方法と、Bird.prototypeプロパティに直接追加する方法に違いはないように聞こえるかもしれません。まだ正常に動作するためですか?

しかし、これは完全に真実ではありません。ここでの違いを完全に理解していないため、まだ詳細については触れません。しかし、このテーマに関する洞察をさらに集めて、このチュートリアルを更新する予定です。

(プロトタイプのベテランからのコメントは大歓迎です!)

すべての鳥が似ているわけではありません

オブジェクト継承のポイントは、そのクラスのすべての子が自動的に継承するすべてのプロパティとメソッドを定義する1つの共通クラスを使用することです。これにより、コードが短くなり、メモリが節約されます。

(すべての子オブジェクトで同じプロパティとメソッドを個別に定義し直すことを想像してください。2倍のメモリが必要になります。)

いくつかの異なる種類の鳥を作りましょう。それらはすべて、まだメインのBirdクラスから継承されているため、飛ぶ、歩く、lay_eggsを実行できますが、それぞれの固有の鳥​​のタイプは、そのクラスに固有の独自のメソッドを追加します。たとえば、オウムだけが話すことができます。パズルを解くことができるのはカラスだけです。鳴き鳥だけが歌うことができます。

オウム
オウムを作成して、Birdから継承します。

Parrotは、Birdと同様に通常のコンストラクター関数です。

違いは、独自のメソッドをアタッチする前に、Bird.callでBirdのコンストラクターを呼び出し、Parrotのthisコンテキストを渡すことです。 Bird.callは、そのプロパティとメソッドをすべてParrotに追加するだけです。それに加えて、独自のメソッドであるtalkも追加しています。

オウムは飛ぶ、歩く、卵を産む、話すことができるようになりました!しかし、Parrot自体の内部でfly walkおよびlay_eggsメソッドを定義する必要はありませんでした。

カラス
同様に、Ravenを作成し、Birdから継承します。

レイヴンはパズルを解くことができるという点でユニークです。

鳴き鳥
次に、Songbirdを作成し、Birdから継承します。

鳴き鳥は歌うことができます。

鳥のテスト

独自の能力を持つさまざまな鳥をたくさん作成しました。見てみましょう
彼らはできる!これまでは、クラスを定義して、
階層関係。

オブジェクトを操作するには、それらをインスタンス化する必要があります。

元のBirdコンストラクタを使用してスズメをインスタンス化します。

すずめは、これらのすべてのメソッドを定義するBirdから継承されているため、飛ぶ、歩く、卵を産むことができます。

しかし、スズメは話すことができません。オウムではないからです。

Parrotクラスからインコを作成しましょう:

ParrotはBirdから継承されているため、そのすべてのメソッドを取得します。インコは話すユニークな能力を持っていますが、歌うことはできません! singメソッドは、Songbirdタイプのオブジェクトでのみ使用できます。 Songbirdクラスからムクドリを継承しましょう:

最後に、ワタリガラスを作成して、いくつかのパズルを解きましょう。

クラスと拡張キーワードの使用

ES5スタイルのコンストラクターは少し面倒です。

幸いなことに、クラスがあり、キーワードを拡張して、前のセクションで行ったのとまったく同じことを実現します。

クラスは関数を置き換えます

extendsとsuper()は、前の例のBird.callを置き換えます。

親クラスのコンストラクターを呼び出すsuper()を使用する必要があることに注意してください。

この構文は、はるかに管理しやすいように見えます!

これで、オブジェクトをインスタンス化するだけで済みます。

概要

クラスの継承は、オブジェクトの階層を確立するのに役立ちます。

クラスは、アプリケーションの設計とアーキテクチャの基本的な構成要素です。彼らはコードでの作業をもう少し人間にします。

もちろん、バードは単なる例です。実際のシナリオでは、作成しようとしているアプリケーションの種類に基づいたものになります。

車両クラスは、Motorcycle、Car、またはTankの親になることができます。

魚は、サメ、金魚、パイクなどを継承するために使用できます。

継承は、よりクリーンなコードを記述し、親オブジェクトを再利用して、繰り返しオブジェクトのプロパティとメソッド定義のメモリを節約するのに役立ちます。