JavaScriptは強力なオブジェクト指向(OOP)言語です。 しかし、他の多くの言語とは異なり、プロトタイプに基づいたOOPモデルを使用しているため、多くの開発者にとってその構文は珍しいものです。 さらに、JavaScriptは関数をファーストクラスオブジェクトとして機能するため、これらの概念に精通していないプログラマを混乱させる可能性があります。 TypeScriptなどの派生言語を使用して回避できます。TypeScriptは、使い慣れた構文を持ち、追加機能を提供します。 しかし、そのような言語はまだ純粋なJavaScriptにコンパイルされており、それを知っているだけでは、実際にどのように機能するか、いつ使用するのが適切かを理解するのに役立ちません。
この記事で説明する内容:
- 名前空間。
- オブジェクト
- オブジェクトリテラル。
- コンストラクター関数。
- 継承
名前空間
ネットワークにはサードパーティ製のライブラリ、フレームワーク、依存関係が登場しているため、グローバル名前空間のオブジェクトと変数の衝突を避けたい場合、名前空間定義はJavaScript開発に不可欠です。
残念ながら、JSには名前空間定義の組み込みサポートがありませんが、オブジェクトを使用して同じ結果を得ることができます。 実装するさまざまなパターンがありますが、最も一般的なもの、つまりネストされた名前空間のみを見ていきます。
このパターンは、オブジェクトリテラルを使用して機能をパッケージにアセンブルし、一意のアプリケーション固有の名前を付けます。 グローバルオブジェクトを作成し、それを変数に割り当てることから開始できます。
var MyApp = MyApp || {};
同じ手法を使用して、名前空間を作成できます。
MyApp.users = MyApp.user || {};
コンテナを作成したら、それを使用してメソッドとプロパティを定義し、衝突の危険なしにそれらをグローバル名前空間に適用できます。
MyApp.users = { // existingUsers: [...], // renderUsersHTML: function() { ... } };
JavaScriptネームスペース定義パターンの詳細については、「JavaScriptの必須ネームスペースパターン」を参照してください。
オブジェクト
すでにJavaScriptでコードを記述している場合、ある程度オブジェクトを使用します。 JavaScriptには3つの異なるタイプのオブジェクトがあります。
ネイティブオブジェクト
ネイティブオブジェクトは言語仕様の一部です。 これらは、コードが実行されているクライアントに関係なく使用できます。 例:配列、日付、および数学。 ネイティブオブジェクトの完全なリスト 。
var users = Array(); // Array —
ホストオブジェクト
ネイティブのものとは異なり、ホストオブジェクトは、コードが実行されるクライアントのおかげで利用可能になります。 さまざまなクライアントで、ほとんどの場合、さまざまなホストオブジェクトと対話できます。 たとえば、ブラウザのコードを記述すると、ウィンドウ、ドキュメント、場所、および履歴が提供されます。
document.body.innerHTML = 'Hello'; // document — -
ユーザーオブジェクト
寄稿オブジェクトとも呼ばれるカスタムオブジェクトは、実行時に定義される独自のオブジェクトです。 JSでオブジェクトを宣言するには2つの方法があり、それらについてさらに検討します。
オブジェクトリテラル
名前空間の定義の章でオブジェクトリテラルについては既に触れました。 次に説明しましょう。オブジェクトリテラルは、中括弧で囲まれた名前と値のペアのコンマ区切りリストです。 これらのリテラルにはプロパティとメソッドを含めることができ、JSの他のオブジェクトと同様に、関数に渡してそれらから返すことができます。 オブジェクトリテラルの例:
var dog = { // breed: 'Bulldog', // bark: function() { console.log(“Woof!”); }, }; // dog.bark();
オブジェクトリテラルはシングルトーンです。 ほとんどの場合、コードをカプセル化し、グローバルスコープ(名前空間)の変数やオブジェクトとの衝突を回避するためにコードをカプセル化し、プラグインやオブジェクトに構成を転送するために使用されます。
オブジェクトリテラルは便利ですが、インスタンス化することも、継承することもできません。 これらの機能が必要な場合は、JSでオブジェクトを作成する別の方法を検討する必要があります。
コンストラクター関数
JavaScriptでは、関数は最初のクラスのオブジェクトと見なされます。つまり、他のエンティティで利用可能な同じ操作をサポートします。 言語の現実では、これは関数が実行時に構築され、引数として渡され、他の関数から返され、変数に割り当てられることを意味します。 さらに、独自のプロパティとメソッドを持つことができます。 これにより、インスタンス化および継承可能なオブジェクトとして関数を使用できます。
コンストラクター関数を使用してオブジェクト定義を使用する例:
function User( name, email ) { // this.name = name; this.email = email; // this.sayHey = function() { console.log( “Hey, I'm “ + this.name ); }; } // var steve = new User( “Steve”, “steve@hotmail.com” ); // steve.sayHey();
コンストラクター関数の作成は、1つの例外を除いて正規表現の作成に似ています。プロパティとメソッドを宣言するためにthisキーワードを使用します。
新しいキーワードを使用してコンストラクター関数をインスタンス化することは、従来のクラスベースのプログラミング言語でオブジェクトをインスタンス化することに似ています。 ただし、一見すると明らかではない問題が1つあります。
newキーワードを使用してJSで新しいオブジェクトを作成する場合、関数ブロックを繰り返し実行します。これにより、スクリプトはメソッドごとに毎回匿名関数を宣言します。 その結果、プログラムは必要以上のメモリを消費し、プログラムのサイズによってはパフォーマンスに深刻な影響を与える可能性があります。
幸いなことに、グローバルスコープを汚染することなく、メソッドをコンストラクター関数にアタッチする方法があります。
メソッドとプロトタイプ
JavaScriptはプロトタイプ言語です。つまり、プロトタイプをオブジェクトテンプレートとして使用できます。 これにより、アプリケーションの規模が拡大しても、匿名関数によるトラップを回避できます。 PrototypeはJavaScriptの特別なプロパティであり、オブジェクトに新しいメソッドを追加できます。
以下は、プロトタイプを使用して書き直された例のバリアントです。
function User( name, email ) { // this.name = name; this.email = email; } // User.prototype.sayHey = function() { console.log( “Hey, I'm “ + this.name ); } // var steve = new User( “Steve”, “steve@hotmail.com” ); // steve.sayHey();
この例では、
sayHey()
はUserオブジェクトのすべてのインスタンスで共有されます。
継承
プロトタイプは、プロトタイプチェーンの一部として継承にも使用されます。 JSでは、各オブジェクトにプロトタイプがあり、プロトタイプはもう1つのオブジェクトであるため、プロトタイプもあります...値が
null
プロトタイプに到達するまで、これがチェーンの最後のリンクです。
メソッドまたはプロパティを参照する場合、JSはオブジェクト定義で指定されているかどうかを確認し、指定されていない場合はプロトタイプを確認して定義を探します。 プロトタイプで見つからない場合は、検出されるかチェーンの最後に達するまでプロトタイプチェーンに沿って進みます。
仕組みは次のとおりです。
// function User( name, email, role ) { this.name = name; this.email = email; this.role = role; } User.prototype.sayHey = function() { console.log( “Hey, I'm an “ + role); } // editor user function Editor( name, email ) { // Call Constructor User Editor User.call(this, name, email, "admin"); } // User Editor Editor.prototype = Object.create( User.prototype ); // Editor User var david = new Editor( "David", "matthew@medium.com" ); david.sayHey();
プロトタイプの継承に慣れるには時間がかかる場合がありますが、バニラJavaScriptの高みに到達するには、この概念を習得することが重要です。 多くの場合、言語の弱点の1つと呼ばれていますが、プロトタイプ継承モデルは実際には従来のモデルよりも強力です。 たとえば、プロトタイプの上に古典的なモデルを構築することは難しくありません。
ECMAScript 6では、クラスを実装する新しいキーワードセットが導入されています。 これらの構造はクラスベースの言語と同じように見えますが、同じものではありません。 JavaScriptはまだプロトタイプベースです。
* * *
JavaScriptは長い間開発されてきましたが、その間、さまざまな慣行が導入されており、現代の標準では避けるべきです。 ES2015の出現により、状況はゆっくりと変化し始めましたが、多くの開発者は依然としてコードの関連性を危うくする古い方法を固守しています。 JavaScriptでOOPの概念を理解して適用することは、堅牢なコードを書くために重要です。この簡単な紹介がこれに役立つことを願っています。