From 149b599368b4e27cf7d05f270a43519f599372fd Mon Sep 17 00:00:00 2001 From: MDN Date: Tue, 18 Jan 2022 00:57:02 +0000 Subject: [CRON] sync translated content --- .../objects/classes_in_javascript/index.html | 413 +++++++++++++++++++++ .../javascript/objects/inheritance/index.html | 412 -------------------- .../objects/object-oriented_js/index.html | 291 --------------- 3 files changed, 413 insertions(+), 703 deletions(-) create mode 100644 files/ja/learn/javascript/objects/classes_in_javascript/index.html delete mode 100644 files/ja/learn/javascript/objects/inheritance/index.html delete mode 100644 files/ja/learn/javascript/objects/object-oriented_js/index.html (limited to 'files/ja/learn/javascript') diff --git a/files/ja/learn/javascript/objects/classes_in_javascript/index.html b/files/ja/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..b0802b7072 --- /dev/null +++ b/files/ja/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,413 @@ +--- +title: JavaScript での継承 +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - Article + - CodingScripting + - Inheritance + - JavaScript + - OOJS + - OOP + - Object + - Prototype + - l10n:priority + - 初心者 + - 学習 +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
+ +

OOJS のぞっとするような細部はほとんど説明されたので、ここでは”親”クラスからの機能を継承する”子供”のオブジェクトクラス (コンストラクタ) の生成方法について解説します。さらに、いつ、どこで OOJS を使うかについてのアドバイスを提示し、最新の ECMAScript の構文でクラスがどのように扱われるかを見ていきます。

+ + + + + + + + + + + + +
前提知識基本的なコンピュータの知識および利用能力、HTML と CSS への基本的な理解、JavaScript の基本 (第一歩構成要素を参照) と OOJS の基本 (オブジェクト入門) に慣れている。
目的:JavaScript でどのように継承ができるようになっているかを理解していること。
+ +

プロトタイプでの継承

+ +

ここまで動作している継承 ー プロトタイプチェーンがどのように動作するか、どのようにメンバーが繋がるチェーンから継承されるのかを見てきました。しかし、これらの大半はブラウザーの組み込み関数で実行されています。我々が他のオブジェクトから継承したオブジェクトを作成するには JavaScript でどのようにするのでしょうか。

+ +

具体的な例を使ってどのようの継承が行われているかを見てゆきましょう。

+ +

さあ始めてみよう

+ +

まず、oojs-class-inheritance-start.html ファイルをローカルにコピーしましょう (あるいはライブ版の実行でも確認できます)。ここでこのモジュールで幅広く使用されてきた Person() というコンストラクタの例を見つけることができます。わずかな違いがあって、コンストラクタ内部にプロパティのみが定義されています。

+ +
function Person(first, last, age, gender, interests) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+};
+ +

メソッドはすべてコンストラクタのプロトタイプとして定義されています。例えば、

+ +
Person.prototype.greeting = function() {
+  alert('Hi! I\'m ' + this.name.first + '.');
+};
+ +
+

注意: ソースコードに、bio() と farewell() が定義されています。後ほどこれらのメソッドがどのようにほかのコンストラクタで継承されるのかを確認します。

+
+ +

Teacher クラスを作成したい場合を考えましょう。これは最初のオブジェクト指向の特徴にて述べたもののようなクラスで、Person からすべてのメンバーを継承しますが、次のものも内包しています。

+ +
    +
  1. 新しいプロパティの subject — これはその先生の教える科目を格納します。
  2. +
  3. 上書きされた greeting() メソッド、標準の greeting() メソッドよりわずかに固く感じられる — 学校で生徒に語りかける先生により相応しい。
  4. +
+ +

Teacher() コンストラクタの機能を定義しよう

+ +

われわれのまずすべき事は Teacher() コンストラクタを作成する事です — 以下に続くコードを既存コードの下に追加してください。

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  Person.call(this, first, last, age, gender, interests);
+
+  this.subject = subject;
+}
+ +

これは多くの点で Person コンストラクタと似ていますが、これまでに見てきたものと異なったものがあります—  call() 関数です。この関数は基本的に別の場所で定義された関数を、しかし現在のコンテキストで呼び出すことができます。最初の引数は関数を実行するときに使用したい this の値を指定します、また他の引数は実行される関数に渡されるべき値です。

+ +

Teacher() コンストラクタは継承元の Person() コンストラクタと同じ引数を取りたいため、 call() を呼び出して、すべての引き数を引数として渡します。

+ +

コンストラクタの最後の行は、先生が行うべきであり、一般の人が持たない新たな subject(授業) のプロパティを定義しています。

+ +

注意として、下記のソースのように、このようにシンプルにも書けます。

+ +
function Teacher(first, last, age, gender, interests, subject) {
+  this.name = {
+    first,
+    last
+  };
+  this.age = age;
+  this.gender = gender;
+  this.interests = interests;
+  this.subject = subject;
+}
+ +

しかしながらこれはただ改めてプロパティを再定義しているだけで、 Person() から継承していません、そのため、説明しようとしたポイントが伝わりません。またコード行数が多くもなります。

+ +

引数なしのコンストラクタからの継承

+ +

もし継承したコンストラクタがパラメータからプロパティの値を取得しない場合、 call() の呼び出しで追加の引数を指定する必要がないことを示しておきます。そのため、例えば、このような本当にシンプルなものがある場合、

+ +
function Brick() {
+  this.width = 10;
+  this.height = 20;
+}
+ +

このように書くことで width と height プロパティを継承することができます(もちろん、下に挙げる数行のステップの様にすることもできます)。

+ +
function BlueGlassBrick() {
+  Brick.call(this);
+
+  this.opacity = 0.5;
+  this.color = 'blue';
+}
+ +

 call() の中に this だけを記載していることに注意して下さい— 引数を介して親より設定されるどのプロパティも継承しないので他の引数は不要です。

+ +

Teacher()のプロトタイプ とコンストラクタの参照への設定方法

+ +

今まではすべて順調でしたが、1点問題があります。新しいコンストラクタを定義して、その中に 1 つの prototype プロパティを持たせ、これはデフォルトでただ自身のコンストラクタ関数への参照を保持しています。Person のコンストラクタの prototype プロパティへのメソッド群は持っていません。このことを見てみたいのならば Object.getOwnPropertyNames(Teacher.prototype)  をテキスト入力フィールドや JavaScript コンソールへ入力を試してみてください。そして再度入力する時には、Teacher を Person で置き換えてみてください。新しいコンストラクタもそれらのメソッドを継承していません。このことを確認するために、Person.prototype.greeting と Teacher.prototype.greeting の出力結果を比べてみてください。Person()  のプロトタイプに定義されたメソッドを継承するために Teacher() を生成する必要があります。ではどのようにすればよいのでしょうか。

+ +
    +
  1. 前回追加した部分の下に以下の行を追加してみましょう: +
    Teacher.prototype = Object.create(Person.prototype);
    + ここで我々に馴染み深い create() に再度助けてもらいましょう。この場合に新しいオブジェクトを作ってそれを Teacher.prototype の値とするのに使います。新しいオブジェクトは Person.prototype を自身のプロトタイプとして保持し、それがゆえに(必要となる時には) Person.prototype 上で利用できるすべてのメソッドを継承します。
  2. +
  3. 先に進む前にもう一つやることがあります。
    + 最後の行を追加した後、Teacher.prototypeconstructor プロパティは Person() と同じになりました。なぜなら、Person.prototype からプロパティを継承するオブジェクトを参照するように Teacher.prototype を設定しただけだからです。コードを保存し、ブラウザでページを読み込み、コンソールに Teacher.prototype.constructor と入力して確認してみてください。
  4. +
  5. これは問題になるかもしれません、なので以下の内容をすぐに設定しましょう。 ソースコードにまた戻って最後に以下の行を追加しましょう。 +
    Object.defineProperty(Teacher.prototype, 'constructor', {
    +    value: Teacher,
    +    enumerable: false, // 'for in'ループで現れないようにする
    +    writable: true });
    +
  6. +
  7. ソースコードを保存およびページの再読み込みを行って、 Teacher.prototype.constructor と入力したならば Teacher() と返すでしょう、希望した通りに Person() から継承することができました!
  8. +
+ +

Teacher() に greeting() ファンクションを付け加える

+ +

コードを完成させる前に、Teacher() コンストラクタに新たに greeting() 関数を追加する必要があります。

+ +

このようにする一番簡単な方法は Teacher() のプロトタイプに定義することです — コードの最後に以下のコードを追加します。

+ +
Teacher.prototype.greeting = function() {
+  let prefix;
+
+  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
+    prefix = 'Mr.';
+  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
+    prefix = 'Mrs.';
+  } else {
+    prefix = 'Mx.';
+  }
+
+  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
+};
+ +

これは性別を基にした適切な敬称を使う教師の挨拶を通知します。条件文を使うことで実現します。

+ +

例を試してみよう

+ +

これまでのコードをすべて入力し終えているなら、ソースコード(もしくはあなたの用意した同じようなコードに)の最後に続けて Teacher() からオブジェクトインスタンスを生成してみましょう。

+ +
let teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
+ +

保存し、再読み込みをしたなら、新たな teacher1 オブジェクトのプロパティとメソッドにアクセスしてみましょう、例えば。

+ +
teacher1.name.first;
+teacher1.interests[0];
+teacher1.bio();
+teacher1.subject;
+teacher1.greeting();
+teacher1.farewell();
+ +

これらはすべて正常に動作するはずです。1, 2, 3, 6 行目のクエリは、ジェネリックな Person() コンストラクタ (クラス) から継承されたメンバにアクセスします。4 行目のクエリは、より特殊な Teacher() コンストラクタ (クラス) でのみ利用可能なメンバにアクセスしています。5 行目のクエリは Person() から継承したメンバにアクセスしていますが、Teacher() には同じ名前の独自のメンバがあるため、そのメンバにアクセスしています。

+ +
+

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(ライブ版も参照)を比較してみてください。

+
+ +

ここで述べている手法は JavaScript でクラスを継承する唯一の方法ではなく、問題なく動作し、JavaScript でのどのように実装するかの良いアイデアを提示しています。

+ +

また JavaScript でより明瞭に継承を行えるようにした新しい{{glossary("ECMAScript")}}の機能(Classes を参照)にも興味を持つかもしれません。ここではそれらについて言及はしませんでした、それはまだブラウザー間で幅広くサポートされていないためです。一連の記事で検討してきた他のコード構造はすべて、IE9 やそれ以前のバージョンといった、はるか以前よりサポートされており、それより早くからのサポートを確認する方法となります。

+ +

一般的な方法は JavaScript ライブラリを使用することです — よく知られた選択肢のうちの大部分は、よりたやすく素早く利用できる簡易な機能セットを持っています。例えば CoffeeScript は class, extends などを提供します。

+ +

追加の特訓

+ +

OOP 理論のセクションでは、概念として Student クラスも取り上げました。このクラスは Person のすべての機能を継承しており、また Person とは異なる Teacher の greeting よりもはるかにくだけた greeting() メソッドを持っています。このセクションで生徒の挨拶がどのように見えるかを見て、Person() のすべての機能を継承し、異なる greeting() 関数を実装した独自の Student() コンストラクタを実装してみてください。

+ +
+

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(動作するライブ版も参照)を比較してみてください。

+
+ +

Object メンバーの概要

+ +

要約すると、気になるプロパティ/メソッドは4種類あります。

+ +
    +
  1. コンストラクタ関数の内部で定義され、オブジェクトインスタンスに与えられるもの。独自のカスタムコードでは、コンストラクタの内部で this.x = x 型の行を使用して定義されたメンバです。組み込みのブラウザコードでは、オブジェクトインスタンス (通常は new キーワードを使用してコンストラクタを呼び出すことで作成されます。例:  let myInstance = new myConstructor()) のみが利用可能なメンバです
  2. +
  3. コンストラクタ自身で直接定義されたもので、コンストラクタ上でのみ利用可能なもの。これらは一般的に組み込みのブラウザオブジェクトでのみ利用可能であり、インスタンスではなくコンストラクタに直接連結されていることで認識されます。たとえば Object.keys() などです。これらは静的プロパティ/メソッドとしても知られています
  4. +
  5. コンストラクタのプロトタイプに定義されているもので、すべてのインスタンスに継承され、オブジェクトクラスを継承しているもの。これらには、コンストラクタの prototype プロパティに定義されている任意のメンバ (例: myConstructor.prototype.x()) が含まれます
  6. +
  7. これらは、上で見たようにコンストラクタがインスタンス化されたときに作成されるオブジェクト (例えば let teacher1 = new Teacher('Chris'); の後に teacher1.name)、またはオブジェクトリテラル (let teacher1 = { name : 'Chris' } の後に teacher1.name) のいずれかであることができます
  8. +
+ +

もしどれがどれを指すかを区別できないのであれば、まだ気にしないでください — あなたはまだ学習中で、実践を通じて精通することでしょう。

+ +

ECMAScript 2015 のクラス

+ +

ECMAScript 2015では、C++ や Java のクラスに似た、より簡単で洗練された構文を使用して再利用可能なクラスを記述する方法として、JavaScript にクラス構文を導入しています。このセクションでは、Person と Teacher の例をプロトタイプの継承からクラスに変更して、どのようにして行うかを示します。

+ +
+

メモ: この近代的なクラスの作成方法は現在のすべてのブラウザでサポートされていますが、この構文をサポートしていないブラウザ (特に Internet Explorer) をサポートする必要があるプロジェクトで作業する場合に備えて、基本的なプロトタイプの継承の仕組みについて知っておくことはまだ価値があります。

+
+ +

Person の例を class-style で書き直したバージョンを見てみましょう:

+ +
class Person {
+  constructor(first, last, age, gender, interests) {
+    this.name = {
+      first,
+      last
+    };
+    this.age = age;
+    this.gender = gender;
+    this.interests = interests;
+  }
+
+  greeting() {
+    console.log(`Hi! I'm ${this.name.first}`);
+  };
+
+  farewell() {
+    console.log(`${this.name.first} has left the building. Bye for now!`);
+  };
+}
+ +

classステートメントは、新しいクラスを作成していることを示します。 このブロックの中で、クラスのすべての機能を定義します。

+ + + +

以前と同じようにnew演算子を使用してオブジェクトインスタンスをインスタンス化できるようになりました。

+ +
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
+han.greeting();
+// Hi! I'm Han
+
+let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
+leia.farewell();
+// Leia has left the building. Bye for now
+ +
+

メモ: 内部ではクラスはプロトタイプの継承モデルに変換されています。これはシンタックスシュガーです。しかし、私たちはあなたがそれを書く方が簡単だと考えるだろうと確信しています。

+
+ +

クラス構文による継承

+ +

上記では人を表すクラスを作成しました。彼らはすべての人々に共通の一連の属性を持っています。このセクションでは、特殊なTeacherクラスを作成し、現代のクラス構文を使用してPersonから継承します。これはサブクラス、またはサブクラスの作成と呼ばれます。
+
+ サブクラスを作成するには extends キーワードを使用して、クラスの基底となるクラスをJavaScriptに通知します。

+ +
constructor(first, last, age, gender, interests) {
+   this.name = {
+     first,
+     last
+   };
+   this.age = age;
+   this.gender = gender;
+   this.interests = interests;
+} 
+ +

super() 演算子を constructor() 内の最初の項目として定義することで、コードをより読みやすくすることができます。これは親クラスのコンストラクタを呼び出し、そこに定義されている限り、指定したメンバーをsuper() のパラメータとして継承します。

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+
+    // 科目と学年は教師によって決まっている
+    this.subject = subject;
+    this.grade = grade;
+  }
+}
+ +

Teacherのオブジェクトをインスタンス化するときには、TeacherPersonの両方で定義されたメソッドとプロパティを呼び出すことができます。

+ +
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
+snape.greeting(); // Hi! I'm Severus.
+snape.farewell(); // Severus has left the building. Bye for now.
+snape.age // 58
+snape.subject; // Dark arts
+ +

Teachers と同じように、基本クラスを変更せずに Person をさらに特化したものにするために、他のサブクラスを作成できます。

+ +
+

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

+
+ +

Getter と Setter

+ +

作成するクラスの属性の値を変更したい場合や、属性の最終値がわからない場合があります。Teacher の例を使用すると、教師が教材を作成する前にどの教科を教えるか分からないことがあります。
+
+ getter や setter でこのような状況を処理できます。
+
+ getter と setter で Teacher クラスを強化しましょう。私たちが最後に見たときと同じようにクラスが始まります。
+
+ getter と setter はペアで動作します。getter は変数の現在の値を返し、対応する setter は変数の値をひとつの値に変更します。
+
+ 変更された Teacher クラスは次のようになります。

+ +
class Teacher extends Person {
+  constructor(first, last, age, gender, interests, subject, grade) {
+    super(first, last, age, gender, interests);
+    // 科目と学年は教師によって決まっている
+    this._subject = subject;
+    this.grade = grade;
+  }
+
+  get subject() {
+    return this._subject;
+  }
+
+  set subject(newSubject) {
+    this._subject = newSubject;
+  }
+}
+ +

上のクラスでは、subject プロパティの getter と setter があります。 Nameプロパティを格納するために _ を使用して別の値を作成します。この規約を使用しないと、get または set を呼び出すたびにエラーが発生します。 この時点で:

+ + + +

以下の例は、動作している2つの機能を示しています。

+ +
// デフォルトの値をチェックする
+console.log(snape.subject) // Returns "Dark arts"
+
+// 値を変更する
+snape.subject = "Balloon animals" // Sets _subject to "Balloon animals"
+
+// 新しい値と一致しているか再度チェックする
+console.log(snape.subject) // Returns "Balloon animals"
+ +
+

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

+
+ +
+

メモ: ゲッターやセッターは、プロパティが要求されたり設定されたりするたびにコードを実行したい場合など、非常に便利な場合があります。しかし、単純なケースでは、ゲッターやセッターを使用しないプレーンなプロパティアクセスで十分です。

+
+ +

JavaScript でいつ継承を使用するのでしょうか?

+ +

特にこの最後の記事を読み終えた後、「うーん、これはややこしいな。」と考えることでしょう。ええ、それは正しい感想です。プロトタイプと継承は JavaScript のもっとも複雑な面のいくつかに当たります、しかし多くの JavaScript の能力と柔軟性はそのオブジェクトの構造と継承に由来します、そしてそれがどのように動作するかは理解するに値します。

+ +

ある意味では、常に継承を使用しています。Web API の様々な機能、文字列や配列といったブラウザーに組み込まれたオブジェクトで定義されているメソッド/プロパティを使用するときはいつも、暗黙の内に継承を使用しています。

+ +

コードに継承を使用していることに関して、特に開始時には、そして小さなプロジェクトでは多分頻繁には使っていないでしょう。不要にも関わらず、継承のためだけにオブジェクトおよび継承を使用するのは時間の浪費です。しかしコードの母体が大きくなればなるほど、継承についての必要性が目に付きやすくなってきます。同じような機能を持ついくつものオブジェクトを作成していることに気付いた場合は、共有機能を持つ汎化オブジェクトタイプを作成し、特化オブジェクトタイプでそれらの機能を継承させるのがお手軽であり、便利です。

+ +
+

注記: プロトタイプチェーンなどを使って JavaScript が動作する方法のために、オブジェクト間での機能の共有をしばしば 移譲 と呼ぶ事があります。特化オブジェクトは汎化オブジェクトタイプから機能的に移譲されています。

+
+ +

継承を使用している時、継承をやたら多いレベルに行わないように、メソッドとプロパティをどこに定義したかを注意深く追跡し続けるようにアドバイスされるでしょう。組み込みブラウザーのオブジェクトのプロトタイプを一時的に変更するコードを書き始めることは可能ですが、実際に良い理由がないのであれば、そうすべきではありません。過剰な継承は終わりない混乱や、そんなコードをデバッグする時は終わりない痛みに繋がりかねません。

+ +

究極的には、オブジェクトは関数やループのような、自身の固有の役割や長所を活かした、コードの再利用の単なる別の形でもあります。もし関連のある変数や関数の一団を作成していることに気付き、それらすべてを追跡して適切にパッケージ化したいのであれば、オブジェクトは良いアイデアです。オブジェクトはまた、ある所から別の所にデータの集合を渡したい場合にも大変便利です。これらの事柄の両方がコンストラクタや継承を使用する事なく達成できます。もしオブジェクトの単一のインスタンスが必要なだけであれば、オブジェクトリテラルを使用するのが多分より良く、確実に継承は必要ないでしょう。

+ +

プロトタイプチェーンを拡張するための代替案

+ +

JavaScript では、上で示したものとは別に、オブジェクトのプロトタイプを拡張する方法がいくつかあります。その他の方法についての詳細は、継承とプロトタイプチェーンの記事を参照してください。

+ +

あなたのスキルをテストしてみましょう!

+ +

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するためのテストがいくつかあります - あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

+ +

まとめ

+ +

この記事は今知っておくべき考えられる OOJS の核となる理論および文法の残りの部分をカバーしています。この時点で、 JavaScript オブジェクトおよび オブジェクト指向プログラミングの基本、プロトタイプとプロトタイプにおける継承、クラス(コンストラクタ)とオブジェクトのインスタンスの生成、クラスへの機能の追加、他のクラスから継承されたサブクラスの生成をどのように行うか、を理解しているでしょう。

+ +

次の記事では JavaScript Object Notaion (JSON) 、つまり  JavaScript オブジェクトを使用して書かれた共通データ交換フォーマット、がどのように動作するかをを見て行きましょう。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/javascript/objects/inheritance/index.html b/files/ja/learn/javascript/objects/inheritance/index.html deleted file mode 100644 index 2964fbe786..0000000000 --- a/files/ja/learn/javascript/objects/inheritance/index.html +++ /dev/null @@ -1,412 +0,0 @@ ---- -title: JavaScript での継承 -slug: Learn/JavaScript/Objects/Inheritance -tags: - - Article - - CodingScripting - - Inheritance - - JavaScript - - OOJS - - OOP - - Object - - Prototype - - 'l10n:priority' - - 初心者 - - 学習 -translation_of: Learn/JavaScript/Objects/Inheritance ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}
- -

OOJS のぞっとするような細部はほとんど説明されたので、ここでは”親”クラスからの機能を継承する”子供”のオブジェクトクラス (コンストラクタ) の生成方法について解説します。さらに、いつ、どこで OOJS を使うかについてのアドバイスを提示し、最新の ECMAScript の構文でクラスがどのように扱われるかを見ていきます。

- - - - - - - - - - - - -
前提知識基本的なコンピュータの知識および利用能力、HTML と CSS への基本的な理解、JavaScript の基本 (第一歩構成要素を参照) と OOJS の基本 (オブジェクト入門) に慣れている。
目的:JavaScript でどのように継承ができるようになっているかを理解していること。
- -

プロトタイプでの継承

- -

ここまで動作している継承 ー プロトタイプチェーンがどのように動作するか、どのようにメンバーが繋がるチェーンから継承されるのかを見てきました。しかし、これらの大半はブラウザーの組み込み関数で実行されています。我々が他のオブジェクトから継承したオブジェクトを作成するには JavaScript でどのようにするのでしょうか。

- -

具体的な例を使ってどのようの継承が行われているかを見てゆきましょう。

- -

さあ始めてみよう

- -

まず、oojs-class-inheritance-start.html ファイルをローカルにコピーしましょう (あるいはライブ版の実行でも確認できます)。ここでこのモジュールで幅広く使用されてきた Person() というコンストラクタの例を見つけることができます。わずかな違いがあって、コンストラクタ内部にプロパティのみが定義されています。

- -
function Person(first, last, age, gender, interests) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-};
- -

メソッドはすべてコンストラクタのプロトタイプとして定義されています。例えば、

- -
Person.prototype.greeting = function() {
-  alert('Hi! I\'m ' + this.name.first + '.');
-};
- -
-

注意: ソースコードに、bio() と farewell() が定義されています。後ほどこれらのメソッドがどのようにほかのコンストラクタで継承されるのかを確認します。

-
- -

Teacher クラスを作成したい場合を考えましょう。これは最初のオブジェクト指向の特徴にて述べたもののようなクラスで、Person からすべてのメンバーを継承しますが、次のものも内包しています。

- -
    -
  1. 新しいプロパティの subject — これはその先生の教える科目を格納します。
  2. -
  3. 上書きされた greeting() メソッド、標準の greeting() メソッドよりわずかに固く感じられる — 学校で生徒に語りかける先生により相応しい。
  4. -
- -

Teacher() コンストラクタの機能を定義しよう

- -

われわれのまずすべき事は Teacher() コンストラクタを作成する事です — 以下に続くコードを既存コードの下に追加してください。

- -
function Teacher(first, last, age, gender, interests, subject) {
-  Person.call(this, first, last, age, gender, interests);
-
-  this.subject = subject;
-}
- -

これは多くの点で Person コンストラクタと似ていますが、これまでに見てきたものと異なったものがあります—  call() 関数です。この関数は基本的に別の場所で定義された関数を、しかし現在のコンテキストで呼び出すことができます。最初の引数は関数を実行するときに使用したい this の値を指定します、また他の引数は実行される関数に渡されるべき値です。

- -

Teacher() コンストラクタは継承元の Person() コンストラクタと同じ引数を取りたいため、 call() を呼び出して、すべての引き数を引数として渡します。

- -

コンストラクタの最後の行は、先生が行うべきであり、一般の人が持たない新たな subject(授業) のプロパティを定義しています。

- -

注意として、下記のソースのように、このようにシンプルにも書けます。

- -
function Teacher(first, last, age, gender, interests, subject) {
-  this.name = {
-    first,
-    last
-  };
-  this.age = age;
-  this.gender = gender;
-  this.interests = interests;
-  this.subject = subject;
-}
- -

しかしながらこれはただ改めてプロパティを再定義しているだけで、 Person() から継承していません、そのため、説明しようとしたポイントが伝わりません。またコード行数が多くもなります。

- -

引数なしのコンストラクタからの継承

- -

もし継承したコンストラクタがパラメータからプロパティの値を取得しない場合、 call() の呼び出しで追加の引数を指定する必要がないことを示しておきます。そのため、例えば、このような本当にシンプルなものがある場合、

- -
function Brick() {
-  this.width = 10;
-  this.height = 20;
-}
- -

このように書くことで width と height プロパティを継承することができます(もちろん、下に挙げる数行のステップの様にすることもできます)。

- -
function BlueGlassBrick() {
-  Brick.call(this);
-
-  this.opacity = 0.5;
-  this.color = 'blue';
-}
- -

 call() の中に this だけを記載していることに注意して下さい— 引数を介して親より設定されるどのプロパティも継承しないので他の引数は不要です。

- -

Teacher()のプロトタイプ とコンストラクタの参照への設定方法

- -

今まではすべて順調でしたが、1点問題があります。新しいコンストラクタを定義して、その中に 1 つの prototype プロパティを持たせ、これはデフォルトでただ自身のコンストラクタ関数への参照を保持しています。Person のコンストラクタの prototype プロパティへのメソッド群は持っていません。このことを見てみたいのならば Object.getOwnPropertyNames(Teacher.prototype)  をテキスト入力フィールドや JavaScript コンソールへ入力を試してみてください。そして再度入力する時には、Teacher を Person で置き換えてみてください。新しいコンストラクタもそれらのメソッドを継承していません。このことを確認するために、Person.prototype.greeting と Teacher.prototype.greeting の出力結果を比べてみてください。Person()  のプロトタイプに定義されたメソッドを継承するために Teacher() を生成する必要があります。ではどのようにすればよいのでしょうか。

- -
    -
  1. 前回追加した部分の下に以下の行を追加してみましょう: -
    Teacher.prototype = Object.create(Person.prototype);
    - ここで我々に馴染み深い create() に再度助けてもらいましょう。この場合に新しいオブジェクトを作ってそれを Teacher.prototype の値とするのに使います。新しいオブジェクトは Person.prototype を自身のプロトタイプとして保持し、それがゆえに(必要となる時には) Person.prototype 上で利用できるすべてのメソッドを継承します。
  2. -
  3. 先に進む前にもう一つやることがあります。
    - 最後の行を追加した後、Teacher.prototypeconstructor プロパティは Person() と同じになりました。なぜなら、Person.prototype からプロパティを継承するオブジェクトを参照するように Teacher.prototype を設定しただけだからです。コードを保存し、ブラウザでページを読み込み、コンソールに Teacher.prototype.constructor と入力して確認してみてください。
  4. -
  5. これは問題になるかもしれません、なので以下の内容をすぐに設定しましょう。 ソースコードにまた戻って最後に以下の行を追加しましょう。 -
    Object.defineProperty(Teacher.prototype, 'constructor', {
    -    value: Teacher,
    -    enumerable: false, // 'for in'ループで現れないようにする
    -    writable: true });
    -
  6. -
  7. ソースコードを保存およびページの再読み込みを行って、 Teacher.prototype.constructor と入力したならば Teacher() と返すでしょう、希望した通りに Person() から継承することができました!
  8. -
- -

Teacher() に greeting() ファンクションを付け加える

- -

コードを完成させる前に、Teacher() コンストラクタに新たに greeting() 関数を追加する必要があります。

- -

このようにする一番簡単な方法は Teacher() のプロトタイプに定義することです — コードの最後に以下のコードを追加します。

- -
Teacher.prototype.greeting = function() {
-  let prefix;
-
-  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
-    prefix = 'Mr.';
-  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
-    prefix = 'Mrs.';
-  } else {
-    prefix = 'Mx.';
-  }
-
-  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
-};
- -

これは性別を基にした適切な敬称を使う教師の挨拶を通知します。条件文を使うことで実現します。

- -

例を試してみよう

- -

これまでのコードをすべて入力し終えているなら、ソースコード(もしくはあなたの用意した同じようなコードに)の最後に続けて Teacher() からオブジェクトインスタンスを生成してみましょう。

- -
let teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');
- -

保存し、再読み込みをしたなら、新たな teacher1 オブジェクトのプロパティとメソッドにアクセスしてみましょう、例えば。

- -
teacher1.name.first;
-teacher1.interests[0];
-teacher1.bio();
-teacher1.subject;
-teacher1.greeting();
-teacher1.farewell();
- -

これらはすべて正常に動作するはずです。1, 2, 3, 6 行目のクエリは、ジェネリックな Person() コンストラクタ (クラス) から継承されたメンバにアクセスします。4 行目のクエリは、より特殊な Teacher() コンストラクタ (クラス) でのみ利用可能なメンバにアクセスしています。5 行目のクエリは Person() から継承したメンバにアクセスしていますが、Teacher() には同じ名前の独自のメンバがあるため、そのメンバにアクセスしています。

- -
-

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(ライブ版も参照)を比較してみてください。

-
- -

ここで述べている手法は JavaScript でクラスを継承する唯一の方法ではなく、問題なく動作し、JavaScript でのどのように実装するかの良いアイデアを提示しています。

- -

また JavaScript でより明瞭に継承を行えるようにした新しい{{glossary("ECMAScript")}}の機能(Classes を参照)にも興味を持つかもしれません。ここではそれらについて言及はしませんでした、それはまだブラウザー間で幅広くサポートされていないためです。一連の記事で検討してきた他のコード構造はすべて、IE9 やそれ以前のバージョンといった、はるか以前よりサポートされており、それより早くからのサポートを確認する方法となります。

- -

一般的な方法は JavaScript ライブラリを使用することです — よく知られた選択肢のうちの大部分は、よりたやすく素早く利用できる簡易な機能セットを持っています。例えば CoffeeScript は class, extends などを提供します。

- -

追加の特訓

- -

OOP 理論のセクションでは、概念として Student クラスも取り上げました。このクラスは Person のすべての機能を継承しており、また Person とは異なる Teacher の greeting よりもはるかにくだけた greeting() メソッドを持っています。このセクションで生徒の挨拶がどのように見えるかを見て、Person() のすべての機能を継承し、異なる greeting() 関数を実装した独自の Student() コンストラクタを実装してみてください。

- -
-

注記: もしここまでの例がうまく動作しないなら、あなたのコードと完成版(動作するライブ版も参照)を比較してみてください。

-
- -

Object メンバーの概要

- -

要約すると、気になるプロパティ/メソッドは4種類あります。

- -
    -
  1. コンストラクタ関数の内部で定義され、オブジェクトインスタンスに与えられるもの。独自のカスタムコードでは、コンストラクタの内部で this.x = x 型の行を使用して定義されたメンバです。組み込みのブラウザコードでは、オブジェクトインスタンス (通常は new キーワードを使用してコンストラクタを呼び出すことで作成されます。例:  let myInstance = new myConstructor()) のみが利用可能なメンバです
  2. -
  3. コンストラクタ自身で直接定義されたもので、コンストラクタ上でのみ利用可能なもの。これらは一般的に組み込みのブラウザオブジェクトでのみ利用可能であり、インスタンスではなくコンストラクタに直接連結されていることで認識されます。たとえば Object.keys() などです。これらは静的プロパティ/メソッドとしても知られています
  4. -
  5. コンストラクタのプロトタイプに定義されているもので、すべてのインスタンスに継承され、オブジェクトクラスを継承しているもの。これらには、コンストラクタの prototype プロパティに定義されている任意のメンバ (例: myConstructor.prototype.x()) が含まれます
  6. -
  7. これらは、上で見たようにコンストラクタがインスタンス化されたときに作成されるオブジェクト (例えば let teacher1 = new Teacher('Chris'); の後に teacher1.name)、またはオブジェクトリテラル (let teacher1 = { name : 'Chris' } の後に teacher1.name) のいずれかであることができます
  8. -
- -

もしどれがどれを指すかを区別できないのであれば、まだ気にしないでください — あなたはまだ学習中で、実践を通じて精通することでしょう。

- -

ECMAScript 2015 のクラス

- -

ECMAScript 2015では、C++ や Java のクラスに似た、より簡単で洗練された構文を使用して再利用可能なクラスを記述する方法として、JavaScript にクラス構文を導入しています。このセクションでは、Person と Teacher の例をプロトタイプの継承からクラスに変更して、どのようにして行うかを示します。

- -
-

メモ: この近代的なクラスの作成方法は現在のすべてのブラウザでサポートされていますが、この構文をサポートしていないブラウザ (特に Internet Explorer) をサポートする必要があるプロジェクトで作業する場合に備えて、基本的なプロトタイプの継承の仕組みについて知っておくことはまだ価値があります。

-
- -

Person の例を class-style で書き直したバージョンを見てみましょう:

- -
class Person {
-  constructor(first, last, age, gender, interests) {
-    this.name = {
-      first,
-      last
-    };
-    this.age = age;
-    this.gender = gender;
-    this.interests = interests;
-  }
-
-  greeting() {
-    console.log(`Hi! I'm ${this.name.first}`);
-  };
-
-  farewell() {
-    console.log(`${this.name.first} has left the building. Bye for now!`);
-  };
-}
- -

classステートメントは、新しいクラスを作成していることを示します。 このブロックの中で、クラスのすべての機能を定義します。

- - - -

以前と同じようにnew演算子を使用してオブジェクトインスタンスをインスタンス化できるようになりました。

- -
let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);
-han.greeting();
-// Hi! I'm Han
-
-let leia = new Person('Leia', 'Organa', 19, 'female', ['Government']);
-leia.farewell();
-// Leia has left the building. Bye for now
- -
-

メモ: 内部ではクラスはプロトタイプの継承モデルに変換されています。これはシンタックスシュガーです。しかし、私たちはあなたがそれを書く方が簡単だと考えるだろうと確信しています。

-
- -

クラス構文による継承

- -

上記では人を表すクラスを作成しました。彼らはすべての人々に共通の一連の属性を持っています。このセクションでは、特殊なTeacherクラスを作成し、現代のクラス構文を使用してPersonから継承します。これはサブクラス、またはサブクラスの作成と呼ばれます。
-
- サブクラスを作成するには extends キーワードを使用して、クラスの基底となるクラスをJavaScriptに通知します。

- -
constructor(first, last, age, gender, interests) {
-   this.name = {
-     first,
-     last
-   };
-   this.age = age;
-   this.gender = gender;
-   this.interests = interests;
-} 
- -

super() 演算子を constructor() 内の最初の項目として定義することで、コードをより読みやすくすることができます。これは親クラスのコンストラクタを呼び出し、そこに定義されている限り、指定したメンバーをsuper() のパラメータとして継承します。

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-
-    // 科目と学年は教師によって決まっている
-    this.subject = subject;
-    this.grade = grade;
-  }
-}
- -

Teacherのオブジェクトをインスタンス化するときには、TeacherPersonの両方で定義されたメソッドとプロパティを呼び出すことができます。

- -
let snape = new Teacher('Severus', 'Snape', 58, 'male', ['Potions'], 'Dark arts', 5);
-snape.greeting(); // Hi! I'm Severus.
-snape.farewell(); // Severus has left the building. Bye for now.
-snape.age // 58
-snape.subject; // Dark arts
- -

Teachers と同じように、基本クラスを変更せずに Person をさらに特化したものにするために、他のサブクラスを作成できます。

- -
-

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

-
- -

Getter と Setter

- -

作成するクラスの属性の値を変更したい場合や、属性の最終値がわからない場合があります。Teacher の例を使用すると、教師が教材を作成する前にどの教科を教えるか分からないことがあります。
-
- getter や setter でこのような状況を処理できます。
-
- getter と setter で Teacher クラスを強化しましょう。私たちが最後に見たときと同じようにクラスが始まります。
-
- getter と setter はペアで動作します。getter は変数の現在の値を返し、対応する setter は変数の値をひとつの値に変更します。
-
- 変更された Teacher クラスは次のようになります。

- -
class Teacher extends Person {
-  constructor(first, last, age, gender, interests, subject, grade) {
-    super(first, last, age, gender, interests);
-    // 科目と学年は教師によって決まっている
-    this._subject = subject;
-    this.grade = grade;
-  }
-
-  get subject() {
-    return this._subject;
-  }
-
-  set subject(newSubject) {
-    this._subject = newSubject;
-  }
-}
- -

上のクラスでは、subject プロパティの getter と setter があります。 Nameプロパティを格納するために _ を使用して別の値を作成します。この規約を使用しないと、get または set を呼び出すたびにエラーが発生します。 この時点で:

- - - -

以下の例は、動作している2つの機能を示しています。

- -
// デフォルトの値をチェックする
-console.log(snape.subject) // Returns "Dark arts"
-
-// 値を変更する
-snape.subject = "Balloon animals" // Sets _subject to "Balloon animals"
-
-// 新しい値と一致しているか再度チェックする
-console.log(snape.subject) // Returns "Balloon animals"
- -
-

メモ: この例は、GitHub で es2015-class-inheritance.html として見つけることができます(これも実際に参照してください)。

-
- -
-

メモ: ゲッターやセッターは、プロパティが要求されたり設定されたりするたびにコードを実行したい場合など、非常に便利な場合があります。しかし、単純なケースでは、ゲッターやセッターを使用しないプレーンなプロパティアクセスで十分です。

-
- -

JavaScript でいつ継承を使用するのでしょうか?

- -

特にこの最後の記事を読み終えた後、「うーん、これはややこしいな。」と考えることでしょう。ええ、それは正しい感想です。プロトタイプと継承は JavaScript のもっとも複雑な面のいくつかに当たります、しかし多くの JavaScript の能力と柔軟性はそのオブジェクトの構造と継承に由来します、そしてそれがどのように動作するかは理解するに値します。

- -

ある意味では、常に継承を使用しています。Web API の様々な機能、文字列や配列といったブラウザーに組み込まれたオブジェクトで定義されているメソッド/プロパティを使用するときはいつも、暗黙の内に継承を使用しています。

- -

コードに継承を使用していることに関して、特に開始時には、そして小さなプロジェクトでは多分頻繁には使っていないでしょう。不要にも関わらず、継承のためだけにオブジェクトおよび継承を使用するのは時間の浪費です。しかしコードの母体が大きくなればなるほど、継承についての必要性が目に付きやすくなってきます。同じような機能を持ついくつものオブジェクトを作成していることに気付いた場合は、共有機能を持つ汎化オブジェクトタイプを作成し、特化オブジェクトタイプでそれらの機能を継承させるのがお手軽であり、便利です。

- -
-

注記: プロトタイプチェーンなどを使って JavaScript が動作する方法のために、オブジェクト間での機能の共有をしばしば 移譲 と呼ぶ事があります。特化オブジェクトは汎化オブジェクトタイプから機能的に移譲されています。

-
- -

継承を使用している時、継承をやたら多いレベルに行わないように、メソッドとプロパティをどこに定義したかを注意深く追跡し続けるようにアドバイスされるでしょう。組み込みブラウザーのオブジェクトのプロトタイプを一時的に変更するコードを書き始めることは可能ですが、実際に良い理由がないのであれば、そうすべきではありません。過剰な継承は終わりない混乱や、そんなコードをデバッグする時は終わりない痛みに繋がりかねません。

- -

究極的には、オブジェクトは関数やループのような、自身の固有の役割や長所を活かした、コードの再利用の単なる別の形でもあります。もし関連のある変数や関数の一団を作成していることに気付き、それらすべてを追跡して適切にパッケージ化したいのであれば、オブジェクトは良いアイデアです。オブジェクトはまた、ある所から別の所にデータの集合を渡したい場合にも大変便利です。これらの事柄の両方がコンストラクタや継承を使用する事なく達成できます。もしオブジェクトの単一のインスタンスが必要なだけであれば、オブジェクトリテラルを使用するのが多分より良く、確実に継承は必要ないでしょう。

- -

プロトタイプチェーンを拡張するための代替案

- -

JavaScript では、上で示したものとは別に、オブジェクトのプロトタイプを拡張する方法がいくつかあります。その他の方法についての詳細は、継承とプロトタイプチェーンの記事を参照してください。

- -

あなたのスキルをテストしてみましょう!

- -

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するためのテストがいくつかあります - あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

- -

まとめ

- -

この記事は今知っておくべき考えられる OOJS の核となる理論および文法の残りの部分をカバーしています。この時点で、 JavaScript オブジェクトおよび オブジェクト指向プログラミングの基本、プロトタイプとプロトタイプにおける継承、クラス(コンストラクタ)とオブジェクトのインスタンスの生成、クラスへの機能の追加、他のクラスから継承されたサブクラスの生成をどのように行うか、を理解しているでしょう。

- -

次の記事では JavaScript Object Notaion (JSON) 、つまり  JavaScript オブジェクトを使用して書かれた共通データ交換フォーマット、がどのように動作するかをを見て行きましょう。

- -

あわせて参照

- - - -

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}

- -

このモジュール

- - diff --git a/files/ja/learn/javascript/objects/object-oriented_js/index.html b/files/ja/learn/javascript/objects/object-oriented_js/index.html deleted file mode 100644 index 387400f5b0..0000000000 --- a/files/ja/learn/javascript/objects/object-oriented_js/index.html +++ /dev/null @@ -1,291 +0,0 @@ ---- -title: 初心者のためのオブジェクト指向 JavaScript -slug: Learn/JavaScript/Objects/Object-oriented_JS -tags: - - Beginner - - Create - - JavaScript - - OOJS - - OOP - - オブジェクト - - オブジェクト指向 - - 学習 - - 記事 -translation_of: Learn/JavaScript/Objects/Object-oriented_JS ---- -
{{LearnSidebar}}
- -
{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}
- -

基礎が片付いたところで、オブジェクト指向 JavaScript (OOJS) について取り上げます。この記事ではオブジェクト指向プログラミング (OOP) の基本的な視点を説明し、 JavaScript がどのようにコンストラクター関数を通じてオブジェクトクラスをエミュレートしているか、またどのようにオブジェクトインスタンスを生成しているかを紹介します。

- - - - - - - - - - - - -
前提知識:基礎的なコンピュータリテラシー、基礎的な HTML と CSS の理解、JavaScript (JavaScript の第一歩JavaScript の構成要素を参照) および OOJS (JavaScript オブジェクトの基本を参照)の基礎知識。
目標:オブジェクト指向プログラミングの基本理論、どのようにそれが JavaScript (「すべてはオブジェクトである」) に関連しているか、どのようにコンストラクターがオブジェクトインスタンスを生成しているかを理解する。
- -

オブジェクト指向プログラミング - その基本

- -

はじめに、オブジェクト指向プログラミング (OOP) とは何か、シンプルかつ高レベルな視点を提示します。シンプルと述べたのは、OOP はあっという間にひどく複雑になり得るためで、現時点でそのすべてを論じてしまうと、助けとなるよりもむしろ混乱を生んでしまうことでしょう。OOP の基本的な考え方は、プログラムの中で扱いたい、現実世界の事物を模るためにオブジェクトを使用すること、またはそうしなければ使うことが難しいあるいは不可能だった機能にアクセスするための、シンプルな方法を提供することです。

- -

オブジェクトは、モデル化しようとしている事物に関する情報および、持たせたい機能や動作を表現する、関連したデータとコードを持つことができます。オブジェクトのデータ (しばしば関数も含む) はオブジェクトのパッケージの中 (名前空間と呼ばれることがある) に適切に格納されます (カプセル化)。オブジェクトは一般に、ネットワークを通じて容易に送信することが可能な、データストアとしても使われます。

- -

オブジェクトのテンプレートを定義する

- -

学校の生徒と教師の情報を表示する、シンプルなプログラムを考えてみましょう。特定のプログラミング言語の文脈ではなく、OOP 一般の理論を眺めていきます。

- -

はじめに、オブジェクト入門の最初の記事にある、人物の包括的なデータや機能を定義した、Person オブジェクトに戻りましょう。ある人物について知り得る事柄は数多くあります (住所、身長、靴のサイズ、DNA 情報、パスポート番号、顕著な人格特性など) が、このケースでは名前、年齢、性別、趣味を表示することに興味があるだけです。また、このデータに基づいた短い自己紹介や、挨拶をさせられるようにもしましょう。これは抽象化 — より複雑な事物を、プログラムの目的に沿って簡単に操作できるように、その最も重要な側面を表現する、シンプルなモデルを作ること — として知られています。

- -

- -

実際のオブジェクトの生成

- -

このクラスから、オブジェクトインスタンスを生成することができます。オブジェクトインスタンスは、クラスで定義されたデータや機能を持ったオブジェクトです。 Person クラスから、何名かの実際の人物を生成します。

- -

- -

クラスからオブジェクトインスタンスが生成されるとき、クラスのコンストラクター関数が生成のために実行されます。クラスからオブジェクトインスタンスが生成される過程をインスタンス化と呼びます。オブジェクトインスタンスは、クラスをインスタンス化したものです。

- -

専門のクラス

- -

このケースで求めているのは、包括的な人物ではなく、より特定のタイプである、教師と生徒です。OOP では、他のクラスを元にした新しいクラスを作ることができます。これらの新しい子クラスは、親クラスからデータやコード機能を継承することができ、すべてのオブジェクトタイプに共通する機能を、重複させるのではなく、再利用することができます。クラス間で機能が異なる場合は、必要に応じて特殊化された機能を直接定義することができます。

- -

- -

これは実に役立ちます。教師と生徒は名前、性別、年齢のように多数の共通機能を共有しており、これらの機能を一度だけ定義すればいいので便利です。異なるクラスで、同じ機能を分けて定義することもでき、その機能の各定義は異なる名前空間に置かれます。例えば、生徒の挨拶は "Yo, I'm [firstName]" (例:Yo, I'm Sam) という形式とし、一方の教師の挨拶は、より形式的に "Hello, my name is [Prefix] [lastName], and I teach [Subject]." (例:Hello, My name is Mr Griffiths, and I teach Chemistry) のように。

- -
-

: 同じ機能を複数のオブジェクトタイプが実装する能力のことを示す用語に、ポリモーフィズムがあります。不思議に感じているかも知れないので念のため。

-
- -

子クラスのオブジェクトインスタンスを生成しましょう。例:

- -

- -

記事の続きでは、OOP 理論が JavaScript でどのように実践されているかを見ていきます。

- -

コンストラクターとオブジェクトインスタンス

- -

JavaScript では、オブジェクトやその機能を定義し初期化するためにコンストラクター関数と呼ばれる特殊な関数を使用します。これは便利です。なぜならオブジェクトをいくつ作成することになるか分からない状況に出くわすでしょうから。コンストラクターは必要な数のオブジェクトを効率的な方法で作成し、必要に応じてデータや関数を付加する手段を提供します。

- -

JavaScript でコンストラクターを通じてクラスを作り、そこからオブジェクトのインスタンスを生成するところを見ていきましょう。まずは、最初のオブジェクトの記事で見た oojs.html ファイルの新しいコピーを、ローカルに作成しておいてください。

- -

シンプルな例

- -
    -
  1. どのように通常の関数で人物を定義できるかを見てみるところから始めましょう。この関数を script 要素の中に加えてください。 - -
    function createNewPerson(name) {
    -  const obj = {};
    -  obj.name = name;
    -  obj.greeting = function() {
    -    alert('Hi! I\'m ' + obj.name + '.');
    -  };
    -  return obj;
    -}
    -
  2. -
  3. この関数を呼び出すことで、新しい人物を生成することができます。次の 3 行をブラウザーの JavaScript コンソールで試してみてください。 -
    const salva = createNewPerson('Salva');
    -salva.name;
    -salva.greeting();
    - これも十分上手くいっていますが、やや長ったらしいです。オブジェクトを生成したいと知っているなら、なぜ明示的に空のオブジェクトを生成し、返すことが必要なのでしょうか?幸いにも、 JavaScript はコンストラクター関数という形で、便利なショートカットを提供してくれます。早速作ってみましょう!
  4. -
  5. 前の関数を、以下のもので置き換えてください。 -
    function Person(name) {
    -  this.name = name;
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  };
    -}
    -
  6. -
- -

コンストラクター関数は、 JavaScript 版のクラスです。それは関数に期待される全ての機能を持っていますが、何も返さないし、明示的にオブジェクトを生成しもしないという点に注意してください。基本的には、プロパティとメソッドを定義するだけです。加えて、 this キーワードが使われていることにも注意してください。基本、オブジェクトインスタンスの 1 つが作成されるときにはいつでも、オブジェクトの name プロパティはコンストラクター呼び出しに渡される name 値と等しくなり、 greeting() メソッドもコンストラクター呼び出しに渡される name 値を使用します。

- -
-

メモ: 通常、コンストラクター関数の名前は大文字で始まります。コードの中で、コンストラクター関数がより容易に認識されるようにするための慣習です。

-
- -

では、オブジェクトを生成するために、どのようにコンストラクターを呼び出したらよいでしょうか?

- -
    -
  1. 次の 2 行を、前のコードの続きに加えてください。 -
    let person1 = new Person('Bob');
    -let person2 = new Person('Sarah');
    -
  2. -
  3. コードを保存し、ブラウザーをリロードして、以下の 4 行を JavaScript コンソールに入れて試してみてください。 -
    person1.name
    -person1.greeting()
    -person2.name
    -person2.greeting()
    -
  4. -
- -

素晴らしい!2 つの新しいオブジェクトが、異なる名前空間の下でページに格納されていることが確認できます。それらのプロパティとメソッドにアクセスするときには、 person1 または person2 を呼び出すことから始めなければなりません。中に含まれている機能は適切にパッケージ化されており、他の機能と衝突しないようになっています。しかしながら、それらは同じように name プロパティと greeting() メソッドが利用可能です。 2 つのオブジェクトはそれぞれ、生成されたときに割り当てられた、自身の name 値を使っていることに注意してください。これが this を使うことがとても重要である理由の 1 つであり、他の値ではなく、自身の値を使っているのです。

- -

コンストラクターをもう一度呼び出してみましょう。

- -
let person1 = new Person('Bob');
-let person2 = new Person('Sarah');
- -

いずれのケースでも、新しいオブジェクトインスタンスを生成したいとブラウザーに伝えるために new キーワードが使われており、その後に、括弧に必要なパラメーターを入れた関数名が続き、その結果が変数に格納されていて、一般的な関数の呼ばれ方とよく似ています。どちらのインスタンスも、次の定義によって生成されています。

- -
function Person(name) {
-  this.name = name;
-  this.greeting = function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  };
-}
- -

新しいオブジェクトが生成された後、 person1 および person2 変数は、次のオブジェクトを格納しています。

- -
{
-  name: 'Bob',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
-
-{
-  name: 'Sarah',
-  greeting: function() {
-    alert('Hi! I\'m ' + this.name + '.');
-  }
-}
- -

コンストラクター関数を呼び出すとき、毎回 greeting() メソッドを定義していることに注意してください。これは理想的ではありません。これを回避するために、代わりにプロトタイプに関数を定義することができます。後で見てみましょう。

- -

最終的なコンストラクターの作成

- -

上で見てきた例は、スタートのためのシンプルな例に過ぎません。次は最終的な Person() コンストラクター関数を作りましょう。

- -
    -
  1. ここまでに挿入したコードを削除し、代わりとなるコンストラクターを追加してください。これはシンプルな例とほぼ同じもので、ほんのわずか複雑になっているだけです。 -
    function Person(first, last, age, gender, interests) {
    -  this.name = {
    -     first : first,
    -     last : last
    -  };
    -  this.age = age;
    -  this.gender = gender;
    -  this.interests = interests;
    -  this.bio = function() {
    -    alert(this.name.first + ' ' + this.name.last + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
    -  };
    -  this.greeting = function() {
    -    alert('Hi! I\'m ' + this.name.first + '.');
    -  };
    -}
    -
  2. -
  3. ではその下に、コンストラクターからオブジェクトインスタンスを生成するため、次の行を追加してください。 -
    let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
    -
  4. -
- -

ちょうど以前行ったように、プロパティやメソッドにアクセスできることを確認できます。 JavaScript コンソールの中でやってみましょう。

- -
person1['age']
-person1.interests[1]
-person1.bio()
-// etc.
- -
-

メモ: もしこの工程で何らかのトラブルがあった場合は、あなたのコードを我々のバージョン (oojs-class-finished.htmlライブサンプルも) と比べてみてください。

-
- -

さらなる練習

- -

まずはじめに、さらにいくつかのオブジェクトを生成する独自の行を追加し、オブジェクトインスタンスのメンバーの取得や設定をしてみてください。

- -

加えて、 bio() メソッドにはいくつかの問題点があります。人物が女性である、あるいは他の優先される性別分類の場合でも、その出力には常に "He" という代名詞が含まれています。また、 bio は interests 配列により多くのものが列挙されていても、2 つの趣味しか含みません。このクラス定義 (コンストラクター) の問題を、あなたはどのように修正することができますか?コンストラクター内に任意のコード (恐らく、いくつかの条件分岐やループが必要となるでしょう) を入れてみてください。性別や、趣味の数が 1、2、あるいは 2 よりも多いかどうかによって、文がどのように構築されるべきか考えてみてください。

- -
-

: もし行き詰まってしまった場合は、GitHub に答えとなるリポジトリ (ライブ) があります。最初はあなた自身で書いてみてください!

-
- -

オブジェクトインスタンスを生成する他の方法

- -

ここまで、オブジェクトインスタンスを生成する 2 つの異なる方法を見てきました。オブジェクトリテラルの宣言と、上で見たコンストラクター関数の使用です。

- -

これで十分かもしれませんが、他にも方法はあります。ウェブを巡る際に遭遇したときに備えて、よく知っておいてください。

- -

Object() コンストラクター

- -

まず最初に、 Object() コンストラクターを新しいオブジェクトの生成のために使うことができます。はい、一般的なオブジェクトにも、空のオブジェクトを生成するコンストラクターがあります。

- -
    -
  1. このコードを JavaScript コンソールに入力してみましょう。 -
    let person1 = new Object();
    -
  2. -
  3. person1 変数に空のオブジェクトが格納されました。このオブジェクトに、ドット記法とブラケット記法を使ってプロパティを追加することができます。次の例を JavaScript コンソールで試してみましょう。 -
    person1.name = 'Chris';
    -person1['age'] = 38;
    -person1.greeting = function() {
    -  alert('Hi! I\'m ' + this.name + '.');
    -};
    -
  4. -
  5. あらかじめプロパティやメソッドを設定するため、Object() コンストラクターに引数としてオブジェクトリテラルを渡すことも可能です。次のコードを JavaScript コンソールで試してみてください。 -
    let person1 = new Object({
    -  name: 'Chris',
    -  age: 38,
    -  greeting: function() {
    -    alert('Hi! I\'m ' + this.name + '.');
    -  }
    -});
    -
  6. -
- -

create() メソッドの使用

- -

コードの順序についてもコンストラクターが助けとなります。コンストラクターを一箇所で作っておくと、必要に応じてインスタンスを生成することができ、それらがどこから来たものであるか、明瞭です。

- -

しかしながら、特に少数のインスタンスのみを生成する場合に、最初にコンストラクターを作らずにインスタンスを生成することを好む人もいます。JavaScript にはそれを可能とする、create() と呼ばれる組み込みメソッドがあります。それにより、既存のオブジェクトを基にして、新しいオブジェクトを生成することができます。

- -
    -
  1. 前のセクションの練習をブラウザーで終えた状態で、こちらを JavaScript コンソールで試してみてください。 -
    let person2 = Object.create(person1);
    -
  2. -
  3. 次は以下のコードです。 -
    person2.name;
    -person2.greeting();
    -
  4. -
- -

person1 を基に person2 が生成され、person2 では同じプロパティとメソッドが利用可能であることを確認することができます。

- -

create() には、IE8 が対応していないという制限があります。つまり、コンストラクターは古いブラウザーに対応したい場合により効果的かもしれません。

- -

いずれ、create() の効果についてより詳細に紹介するつもりです。

- -

あなたのスキルをテストしてみましょう!

- -

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するために、さらにいくつかのテストを見つけることができます。あなたのスキルをテストする: オブジェクト指向 JavaScript を参照してください。

- -

この一連のテストは次の記事で紹介する知識に依存していることに注意してください。なので、試してみる前に、まずそれを読んでみるといいかもしれません。

- -

まとめ

- -

この記事はオブジェクト指向の理論の概略を見てきました。これですべてではありませんが、ここで扱っていることに関する考えを提示しました。加えて、オブジェクトのインスタンスを生成する様々な方法を見始めたところです。

- -

次の記事では、 JavaScript オブジェクトのプロトタイプについて紹介します。

- -

{{PreviousMenuNext("Learn/JavaScript/Objects/Basics", "Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects")}}

- -

このモジュール内の文書

- - -- cgit v1.2.3-54-g00ecf