From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- .../adding_bouncing_balls_features/index.html | 223 +++++++++++ .../ja/learn/javascript/objects/basics/index.html | 277 ++++++++++++++ files/ja/learn/javascript/objects/index.html | 53 +++ .../javascript/objects/inheritance/index.html | 412 +++++++++++++++++++++ files/ja/learn/javascript/objects/json/index.html | 357 ++++++++++++++++++ .../objects/object-oriented_js/index.html | 291 +++++++++++++++ .../objects/object_building_practice/index.html | 314 ++++++++++++++++ .../objects/object_prototypes/index.html | 314 ++++++++++++++++ .../index.html | 101 +++++ 9 files changed, 2342 insertions(+) create mode 100644 files/ja/learn/javascript/objects/adding_bouncing_balls_features/index.html create mode 100644 files/ja/learn/javascript/objects/basics/index.html create mode 100644 files/ja/learn/javascript/objects/index.html create mode 100644 files/ja/learn/javascript/objects/inheritance/index.html create mode 100644 files/ja/learn/javascript/objects/json/index.html create mode 100644 files/ja/learn/javascript/objects/object-oriented_js/index.html create mode 100644 files/ja/learn/javascript/objects/object_building_practice/index.html create mode 100644 files/ja/learn/javascript/objects/object_prototypes/index.html create mode 100644 files/ja/learn/javascript/objects/test_your_skills_colon__object_basics/index.html (limited to 'files/ja/learn/javascript/objects') diff --git a/files/ja/learn/javascript/objects/adding_bouncing_balls_features/index.html b/files/ja/learn/javascript/objects/adding_bouncing_balls_features/index.html new file mode 100644 index 0000000000..8390797c5b --- /dev/null +++ b/files/ja/learn/javascript/objects/adding_bouncing_balls_features/index.html @@ -0,0 +1,223 @@ +--- +title: バウンスボールに機能を追加する +slug: Learn/JavaScript/Objects/Adding_bouncing_balls_features +tags: + - Assessment + - Beginner + - CodingScripting + - JavaScript + - Learn + - OOJS + - Object-Oriented + - Objects + - 'l10n:priority' +translation_of: Learn/JavaScript/Objects/Adding_bouncing_balls_features +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}
+ +

この評価では、前の記事のバウンスボールのデモを出発点として用い、いくつかの面白い機能を新たに追加してもらいます。

+ + + + + + + + + + + + +
前提条件:この評価を試みる前に、このモジュールのすべての記事を学習済みであること。
+

目的:

+
JavaScript オブジェクトとオブジェクト指向のインスタンス生成を理解しているかテストする。
+ +

出発点

+ +

この評価をスタートするためには、私たちの最新記事からローカル PC の新しいディレクトリーに index-finished.htmstyle.cssmain-finshed.js をコピーします。

+ +

または、あなたの評価のために、JSBinGlitch を使うことができます。これらのオンラインエディターに HTML、CSS や JavaScript を貼り付けることができます。もしあなたが使用しているオンラインエディタが、別々の JavaScript/CSS のパネルを持っていない場合は、HTML内の <script>/<style> 要素を使って、インラインで書くことができます。

+ +
+

: もし行き詰った場合は、サポートを依頼してください。このページの下部にある{{anch("Assessment or further help", "評価とさらなる支援")}}セクションを参照してください。

+
+ +

ヒントと tips

+ +

始める前にいくつかの助言です。

+ + + +

プロジェクト概要

+ +

このバウンスボールのデモは面白いですが、ここではもう少しインタラクティブにするため、バウンスボールを捕まえたら食べてしまう、ユーザー制御された邪悪な円を追加します。また、バウンスボールや邪悪な円が継承できる一般的な Shape() オブジェクトを作ることで、あなたのオブジェクト構築スキルも試してみましょう。最後に、残ったボールが数えられるスコアカウンターも追加してみましょう。

+ +

次のスクリーンショットでは、完成したプログラムがどのように見えるかのイメージを掴めるでしょう: 

+ +

+ + + +

さらにヒントを差し上げます。完成デモを見てみましょう。(ソースコードをチラ見しないように!)

+ +

完成までの手順

+ +

次のセクションでは、必要な操作について説明します。

+ +

新しいオブジェクトを作る

+ +

まず初めに、既存の Ball() コンストラクターを Shape() コンストラクターに変更し、新しい Ball() コンストラクターを追加します:

+ +
    +
  1. Shape() コンストラクターは、xyvelX、および、velY プロパティを、Ball() コンストラクターが最初に行ったのと同じ方法で定義する必要がありますが、色とサイズのプロパティは指定しません。
  2. +
  3. また、新しいプロパティとして、ボールが存在するか(邪悪な円に食べられていないか)どうかを追跡するために使用される exists を新しく定義する必要があります。これはブール値 (true/false) である必要があります。
  4. +
  5. Ball() コンストラクターは、xyvelXvelY、および exists プロパティを Shape() コンストラクターから継承する必要があります。
  6. +
  7. また、元の Ball() コンストラクターのように、colorsize プロパティを定義する必要があります。
  8. +
  9. Ball() コンストラクターの prototypeconstructor を適切に設定してください。
  10. +
+ +

ボールの draw()update()、と collisionDetect() メソッドの定義は、前とまったく同じである必要があります。

+ +

また、new Ball() ( ... ) コンストラクターの呼び出しに新しいパラメーターを追加する必要があります。exists パラメーターは 5番目のパラメーターにする必要があり、true の値を指定する必要があります。

+ +

この時点で、コードをリロードしてみてください。再設計されたオブジェクトで、前と全く同じように動作するはずです。

+ +

EvilCircle() の定義

+ +

さあ、悪者 EvilCircle() の出番です! 私たちのゲームに邪悪な円は1つしか登場しませんが、練習のためにあえて、Shape() から継承するコンストラクターを使用して定義します。後で、他のプレイヤーによって制御される円、あるいは、コンピューター制御の別の邪悪な円をいくつか加えたいと思うかもしれません。おそらく、あなたは単一の邪悪な円の世界を引き継いでいくつもりはないでしょうが、今回の評価のためにはこれで十分です。

+ +

EvilCircle() コンストラクターは、x, y, velX, velYexistsShape() から継承しますが、velXvelY は常に20です。

+ +

これは Shape.call(this, x, y, 20, 20, exists);のように呼び出します。

+ +

次のように、独自のプロパティも定義する必要があります:

+ + + +

ここでも、継承したプロパティをコンストラクターのパラメーターとして定義し、prototypeconstractor のプロパティを正しく設定することを忘れないでください。

+ +

EvilCircle() のメソッドの定義

+ +

EvilCircle() には、以下に示す 4 つのメソッドがあります。

+ +

draw()

+ +

このメソッドは、Ball()draw() メソッドと同じく、キャンバス上にオブジェクトインスタンスを描画するという目的を持ちます。とても良く似た動作をするので、Ball.prototype.draw の定義をコピーすることから始めます。次に、以下の変更を行います。

+ + + +

checkBounds()

+ +

このメソッドは、Ball()update() 関数の最初の部分と同じ機能、すなわち、邪悪な円が画面の端から出そうになったら出ないようにする機能を持ちます。先ほどと同様に、Ball.prototype.update 定義をほぼコピーするだけでできますが、いくつか変更する必要があります。

+ + + +

setControls()

+ +

このメソッドは、onkeydown イベントリスナーを window オブジェクトに追加し、特定のキーボードキーが押されたときに、邪悪な円を動かします。次のコードブロックは、メソッド定義の中に置く必要があります。

+ +
let _this = this;
+window.onkeydown = function(e) {
+    if (e.key === 'a') {
+      _this.x -= _this.velX;
+    } else if (e.key === 'd') {
+      _this.x += _this.velX;
+    } else if (e.key === 'w') {
+      _this.y -= _this.velY;
+    } else if (e.key === 's') {
+      _this.y += _this.velY;
+    }
+  }
+ +

キーが押されると、イベントオブジェクトの key プロパティを調べて、どのキーが押されているかを確認します。押されたキーが、指定された4つのキーの 1 つである場合、邪悪な円は左/右/上/下に移動します。

+ +

おまけとして、let _this = this;をこの場所で設定しなければならない理由を教えてください。関数スコープと関係があります。

+ +

collisionDetect()

+ +

このメソッドは Ball()collisionDetect()メソッドと非常によく似た方法で動作するので、そのコピーをこの新しいメソッドの基礎として使用することができます。しかし、いくつかの違いがあります。

+ + + +

プログラムに邪悪な円を持ち込む

+ +

さて、邪悪な円を定義したので、実際にそれをシーンに表示させる必要があります。そのためには、loop() 関数をいくつか変更する必要があります。

+ + + +

スコアカウンターの実装

+ +

スコアカウンターを実装するには、次の手順に従います。

+ +
    +
  1. HTML ファイルの{{HTMLElement("h1")}}要素の直下に、"Ball count:" というテキストを含む{{HTMLElement( "p")}}要素を追加します。
  2. +
  3. あなたの CSS ファイルに、次のスタイルを追加します: +
    p {
    +  position: absolute;
    +  margin: 0;
    +  top: 35px;
    +  right: 5px;
    +  color: #aaa;
    +}
    +
  4. +
  5. JavaScript では、次の更新を行います: +
      +
    • 段落への参照を格納する変数を作成します。
    • +
    • 何らかの方法で画面上のボールの数をカウントしてください。
    • +
    • ボールをシーンに追加するたびにカウントを増加させ、更新されたボールの数を表示します。
    • +
    • 邪悪な円がボールを食べる(存在を消す)たびにカウントを減らし、更新されたボールの数を表示します。
    • +
    +
  6. +
+ +

評価とさらなる支援

+ +

If you would like your work assessed, or are stuck and want to ask for help:

+ +
    +
  1. Put your work into an online shareable editor such as CodePen, jsFiddle, or Glitch.
  2. +
  3. Write a post asking for assessment and/or help at the MDN Discourse forum Learning category. Your post should include: +
      +
    • A descriptive title such as "Assessment wanted for Adding bouncing balls features".
    • +
    • Details of what you have already tried, and what you would like us to do, e.g. if you are stuck and need help, or want an assessment.
    • +
    • A link to the example you want assessed or need help with, in an online shareable editor (as mentioned in step 1 above). This is a good practice to get into — it's very hard to help someone with a coding problem if you can't see their code.
    • +
    • A link to the actual task or assessment page, so we can find the question you want help with.
    • +
    +
  4. +
+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object_building_practice", "", "Learn/JavaScript/Objects")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/javascript/objects/basics/index.html b/files/ja/learn/javascript/objects/basics/index.html new file mode 100644 index 0000000000..0c97e391d3 --- /dev/null +++ b/files/ja/learn/javascript/objects/basics/index.html @@ -0,0 +1,277 @@ +--- +title: JavaScript オブジェクトの基本 +slug: Learn/JavaScript/Objects/Basics +tags: + - API + - Article + - Beginner + - CodingScripting + - JavaScript + - Syntax + - bracket notation + - dot notation + - instance + - object literal + - this + - オブジェクト + - 学習 + - 理論 +translation_of: Learn/JavaScript/Objects/Basics +--- +
{{LearnSidebar}}
+ +
{{NextMenu("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects")}}
+ +

この記事では、基本的な JavaScript オブジェクトの構文を学び、このコースで以前に見た一部の JavaScript の機能を復習し、すでに提供された多くの機能がオブジェクトであるという事実を再確認します。

+ + + + + + + + + + + + +
前提知識:基本的なコンピューターを操作する能力、基本的な HTML と CSS に対する理解、基本的な JavaScript に親しんでいること(JavaScript の第一歩JavaScript の構成要素を参照してください)。
到達目標:オブジェクト指向プログラミングについての基本的な理論、どのように JavaScript に関連するか、JavaScript の作業を始める方法を理解できること。
+ +

オブジェクトの基本

+ +

オブジェクトとは関連のあるデータと機能の集合です。(機能はたいていは変数と関数で構成されており、オブジェクトの中ではそれぞれプロパティとメソッドと呼ばれます。) どんなものか例を見てみましょう。

+ +

最初に oojs.html ファイルを手元にコピーしてください。このファイルにはちょっとした内容 — ソースコードを書き込むための {{HTMLElement("script")}} 要素が一つ含まれています。このファイルをオブジェクトを書くための元として使います。作業中は開発者ツールの JavaScript コンソールを開いておいて、すぐにコマンドを入力できるようにしておくとよいでしょう。

+ +

他の JavaScript の書き方と同じように、オブジェクトの生成は変数の宣言と初期化から始まります。手始めに、ファイルにある JavaScript コードの下に次のものを書いてみてください。それから保存して再読み込みしましょう。

+ +
const person = {};
+ +

ブラウザの JavaScript コンソールを開いて、person と入力して、 Enter/Return を押してください。以下のいずれかの行に似た結果が得られるはずです。

+ +
[object Object]
+Object { }
+{ }
+ +

よくやりましたね! まずは最初のオブジェクトができました。でもこれだけでは空のオブジェクトであまり役には立ちません。さらにオブジェクトを変更してみましょう。

+ +
const person = {
+  name: ['Bob', 'Smith'],
+  age: 32,
+  gender: 'male',
+  interests: ['music', 'skiing'],
+  bio: function() {
+    alert(this.name[0] + ' ' + this.name[1] + ' is ' + this.age + ' years old. He likes ' + this.interests[0] + ' and ' + this.interests[1] + '.');
+  },
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name[0] + '.');
+  }
+};
+
+ +

保存して再読み込みした後で、JavaScript コンソールにいくつか入力してみましょう。

+ +
person.name
+person.name[0]
+person.age
+person.interests[1]
+person.bio()
+person.greeting()
+ +

オブジェクトから、データと機能を追加することができました。これで簡単な書き方で情報が引き出せます。

+ +
+

: もし動かないようなら、完成版のソースコードと見比べてみてください (完成版: oojs-finished.html さらにライブ版もあります) 。ライブ版は空白の画面ですが、それで OK です。また開発ツールを開いて上記のコマンドを入力してオブジェクトの構造を見てみます。

+
+ +

さて、何が起きているのでしょうか。オブジェクトには複数のメンバーがあり、それぞれに名前がついていて(例えば上の例では nameage)、それぞれに値 (['Bob', 'Smith'] や 32) があります。それぞれの名前と値の組はカンマ ( , ) で区切られていて、名前と値はコロン ( : ) で区切られています。常にそのように書きます。

+ +
const objectName = {
+  member1Name: member1Value,
+  member2Name: member2Value,
+  member3Name: member3Value
+}
+ +

メンバーの値はほとんど何でも大丈夫です。例えば、先ほどの例では文字列、数値、2 つの配列に 2 つの関数でした。最初の 4 つはデータ項目でそのオブジェクトのプロパティと呼ばれます。後ろの 2 つはオブジェクトの持つデータを使用して何かをする関数でオブジェクトのメソッドと呼ばれます。

+ +

このように記号を使って書くオブジェクトは後で出てくるクラスを使用して生成する方法と対比してオブジェクトリテラルと呼ばます。

+ +

オブジェクトリテラルを使用してオブジェクトを生成する方法はとても一般的で、ある法則に則って構造化、関連付けられたデータをやり取りするときによく使います。(例えばサーバーにリクエストを送ったり、データベースに保存したり。) ある一つのオブジェクトを送るほうが複数の項目を何回かに分けて送るよりも効率的で、名前を用いて検索するときなどには、配列よりも扱いやすいときがあります。

+ +

ドットによる記述

+ +

先ほどの例では、オブジェクトのプロパティとメソッドに対して、ドット記法を用いてアクセスしました 。オブジェクト名 (person) は名前空間として機能します。オブジェクト内にカプセル化されたものにアクセスするには、まずこのオブジェクト名を入力する必要があります。次に、ドット ( . ) を書いて、それからアクセスしたい項目を記述します。項目になりうるのは、単純なプロパティの名前や、配列の要素や、そのオブジェクトのメソッドの 1 つへの呼び出しなどです。次に例を示します:

+ +
person.age
+person.interests[1]
+person.bio()
+ +

サブ名前空間

+ +

オブジェクトの内部にさらにほかのオブジェクトを持つことも可能です。例えば、先の例で、name メンバーを、

+ +
name: ['Bob', 'Smith'],
+ +

以下のように変更してみましょう。

+ +
name : {
+  first: 'Bob',
+  last: 'Smith'
+},
+ +

これで簡単にサブ名前空間を作り出すことができました。難しそうに聞こえるかもしれませんが、ただ単に項目をドットを用いて数珠つなぎにつないでいけばいいのです。コンソールで試してください。

+ +
person.name.first
+person.name.last
+ +

重要: この時点で下の書き方をしていたところは、以下のように変えなければなりません。

+ +
name[0]
+name[1]
+ +

を、

+ +
name.first
+name.last
+ +

のようにしなければ、メソッドが動かなくなってしまうでしょう。

+ +

角括弧による記述

+ +

オブジェクトのプロパティにアクセスするもう一つの手段として角括弧を用いた記法があります。

+ +
person.age
+person.name.first
+ +

このように書く代わりに、

+ +
person['age']
+person['name']['first']
+ +

のように書きます。

+ +

これは配列の添え字によく似ています。数字の代わりに、名前を用いて関連付けられたメンバーの値にアクセスするだけで、実はほとんど同じなのです。このようなオブジェクトを連想配列といい、配列が数字によって値を格納しているように、文字列によって値を格納しています。

+ +

オブジェクトメンバーの設定

+ +

今まではオブジェクトメンバーからの引き出す (取得する) 方法だけを見てきましたが、値を設定するメンバーを宣言することで、オブジェクトのメンバーに値を設定 (更新) することもできます。(ドットを使用した書き方でも、角括弧を使用した書き方でも構いません。)

+ +
person.age = 45;
+person['name']['last'] = 'Cratchit';
+ +

これらの行を入力してみて、実際に値が変わったか調べてみましょう。

+ +
person.age
+person['name']['last']
+ +

メンバーの値を設定するのは存在するプロパティやメソッドの更新だけにはとどまりません。まったく新しいメンバーを追加することもできるのです。JS コンソールで次のものを試してみてください。

+ +
person['eyes'] = 'hazel';
+person.farewell = function() { alert("Bye everybody!"); }
+ +

新しいメンバーが追加されたことを確認してみましょう。

+ +
person['eyes']
+person.farewell()
+ +

角括弧での書き方の良いところは、動的にメンバーの値を設定できるだけでなく、メンバーの名前も追加できるところです。例えば、ユーザーの情報として 2 つのテキストフィールドに名前と値を入力してもらい、人により個別のデータを設定したいとします。そういった値を以下のように取得します。

+ +
let myDataName = nameInput.value;
+let myDataValue = nameValue.value;
+ +

そうして、取得したメンバー名と値を次のように person オブジェクトに設定します。

+ +
person[myDataName] = myDataValue;
+ +

この動作を確認するため、先ほどのコードの person オブジェクトの中括弧に続いて、次の行をコードに追加してみてください。

+ +
let myDataName = 'height';
+let myDataValue = '1.75m';
+person[myDataName] = myDataValue;
+ +

そして、保存して再度読み込んで、次の行をテキストボックスに入力してみてください。

+ +
person.height
+ +

上記の方法を使用してオブジェクトにプロパティを追加することは、ドット記法ではできません。ドット記法は、名前を指す変数ではなく、書いたとおりのメンバー名のみ受け入れることができます。

+ +

"this" とは何か

+ +

メソッドの中で、少し見慣れない点に気付いたかもしれません。 次の例でその点について考えてみましょう。

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

"this" とは何だろうと思ったことでしょう。 この this キーワードはコードの中がその中で書かれている、現在のオブジェクトを参照しています。なので、この場合では  person を指します。 なぜ this の代わりに単に person と書かないのでしょうか。 この後 初心者のためのオブジェクト指向 の記事で見るように、コンストラクター等を書き始めるときに this は非常に便利です。メンバーのコンテキストが変わったとき(例えば 2 つの異なる person オブジェクトのインスタンスは、異なる名前を持っているが、greeting メソッドでそれぞれ自身の名前を使用したいとき)に常に正しい値を保証してくれます。

+ +

それでは、簡略化した person オブジェクトを使って、その意味を説明していきましょう。

+ +
const person1 = {
+  name: 'Chris',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+
+const person2 = {
+  name: 'Deepti',
+  greeting: function() {
+    alert('Hi! I\'m ' + this.name + '.');
+  }
+}
+ +

この場合、person1.greeting() は "Hi! I'm Chris." を出力します。一方、person2.greeting() は "Hi! I'm Deepti." を出力します。しかし、どちらの場合も、そのメソッド部分のコードは全く同じです。先に述べたように this はそのコードが中にあるオブジェクトと等しいです — これは手作業でオブジェクトリテラルを書くときにはそれ程便利ではありませんが、動的にオブジェクトを生成する(例えばコンストラクターを使う)ときにとても効果的です。これは、この後によりはっきりとするでしょう。

+ +

ずっとオブジェクトを使ってきた

+ +

これらの例を通して、既に使ってきたドット記述と非常に似ていると考えたかもしれません。なぜならこのコースを通してそのような方法を使用してきたからです。組み込みのブラウザー API や JavaScript オブジェクトを使う例への取り組みを通していつもオブジェクトを使用してきました。なぜならそのような機能は、基本的なカスタム例よりも複雑ではありますが、ここまで見てきたものと全く同種のオブジェクト構造を使うことで構築されているからです。

+ +

だから、このように文字列のメソッドを使うとき:

+ +
myString.split(',');
+ +

String クラスのインスタンスで利用できるメソッドを使用しています。コードの中で文字列を作成するときにはいつでも、その文字列は自動的に String クラスのインスタンスとして生成されます。そしてそのために、いくつかの共通なメソッドやプロパティを使用することができます。

+ +

次の行のようにドキュメントオブジェクトモデルにアクセスするときは、

+ +
const myDiv = document.createElement('div');
+const myVideo = document.querySelector('video');
+ +

Document クラスのインスタンスで使用可能なメソッドを使っています。各ウェブページが読み込まれると、document と呼ばれる Document のインスタンスが作られ、それはウェブページ全体の構造、コンテンツ、その URL 等その他の機能を表現します。もう一度述べますが、これはいくつかの共通なメソッドやプロパティを使用できることを意味します。

+ +

今まで使用してきた、Array や Math 等の、他の多くの組み込みのオブジェクトや API でも全く同じです。

+ +

組み込みのオブジェクトと API では常に自動でオブジェクトのインスタンスが生成される訳ではないことを注意する必要があります。例えば、モダンなブラウザーがシステム通知を発行することを許可する Notifications API では、発行したい各通知のためにコンストラクターを使用した新しいオブジェクトを生成する必要があります。JavaScript コンソールに次を入力してみてください。

+ +
const myNotification = new Notification('Hello!');
+ +

コンストラクターは後の記事でもう一度見ることができます。

+ +
+

: オブジェクトのやり取りをメッセージの受け渡しと考えると便利です。オブジェクトが他のオブジェクトにある処理の実行を要求したとき、そのオブジェクトはメソッドを通じて他のオブジェクトにメッセージを送信して、そして応答を待ちます。ご存知の通り、応答とは返り値のことです。

+
+ +

スキルをテストしましょう!

+ +

この記事の最後に到達しましたが、最も大事な情報を覚えていますか?次に移動する前に、この情報を保持しているか検証するテストがあります — Test your skills: Object basics を見てください。

+ +

おさらい

+ +

お疲れ様でした。最初の JS オブジェクトの記事の終わりまで到達しました。JavaScript のオブジェクトがどのように機能するかについて、良い考えを得ることができたのではないでしょうか。記事では、簡単なオリジナルオブジェクトの作成を含んでいました。オブジェクトは関連するデータと機能を保存する構造として非常に便利であることも理解しなければいけません。もし別々の変数と関数として、person オブジェクトのすべてのプロパティとメソッドを記録していくとすると、非効率でありストレスが溜まります。そして同じ名前の他の変数や関数をクラッシュしてしまう危険性も抱えてしまいます。オブジェクトは有害な方法を避けて、パッケージの中で安全に鍵をして情報を守ってくれます。

+ +

次の記事ではオブジェクト指向プログラミング (OOP) 理論を見ていきます。そして、JavaScript ではそのような素晴らしい技術を使うことができます。

+ +

{{NextMenu("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/javascript/objects/index.html b/files/ja/learn/javascript/objects/index.html new file mode 100644 index 0000000000..65df0e0ece --- /dev/null +++ b/files/ja/learn/javascript/objects/index.html @@ -0,0 +1,53 @@ +--- +title: JavaScript オブジェクト入門 +slug: Learn/JavaScript/Objects +tags: + - Article + - Assesment + - Beginner + - CodingScripting + - Guide + - JavaScript + - Learn + - Objects + - Tutorial + - 学習 +translation_of: Learn/JavaScript/Objects +--- +
{{LearnSidebar}}
+ +

JavaScript では、文字列や配列などの JavaScript のコア機能から、JavaScript の最上部に構築されたブラウザ API まで、ほとんどのものがオブジェクトです。関連する関数や変数を効率的なパッケージにカプセル化して、便利なデータコンテナーとして動作する独自のオブジェクトを作成することもできます。JavaScript のオブジェクトベースの性質を理解することは、言語に関する知識をさらに深め、より効率的なコードを書く場合に重要です。したがって、この役立つモジュールを用意しました。ここではオブジェクトの理論と構文を詳しく説明し、独自のオブジェクトを作成する方法を見ていきます。

+ +

前提条件

+ +

このモジュールを始める前に、 HTML と CSS にいくらか精通している必要があります。JavaScript を始める前に HTML 入門CSS 入門をひととおり学習することをオススメします。

+ +

また、JavaScript オブジェクトを詳細に調べる前に、JavaScript の基本についていくらか精通している必要があります。このモジュールを試す前に、JavaScript の第一歩JavaScript の構成要素を通して学習してください。

+ +
+

注記 : もしあなたが作業しているコンピューター・タブレットやその他のデバイスで自分でファイルを作れない場合は、JSBinThimble といったようなオンラインコーディングプログラムで (ほとんどの場合) 試すことができます。

+
+ +

ガイド

+ +
+
オブジェクトの基本
+
JavaScript オブジェクトについて記載している最初の記事では、基本的な JavaScript オブジェクト構文を見てみます。コースの初期段階で既に見た JavaScript の機能を再び見てみたり、すでに扱っている機能の多くは実際にオブジェクトであるという事実を繰り返し述べています。
+
初心者のためのオブジェクト指向 JavaScript
+
まずオブジェクト指向 JavaScript (OOJS) に焦点を当てます — この記事では、オブジェクト指向プログラミング (OOP) 理論の基本ビューを示し、次に JavaScript がコンストラクタ関数を介してオブジェクトクラスをエミュレートする方法と、オブジェクトインスタンスを作成する方法について説明します。
+
オブジェクトプロトタイプ
+
プロトタイプは、JavaScript オブジェクトが互いに機能を継承するメカニズムであり、古典的なオブジェクト指向プログラミング言語の継承メカニズムとは異なる働きをします。この記事では、その違いを探り、プロトタイプチェーンの仕組みを説明し、prototype プロパティを使用して既存のコンストラクタにメソッドを追加する方法を見ていきます。
+
JavaScript での継承
+
OOJS の大部分の詳細がこれまで説明されているため、この記事では、「親」クラスから機能を継承する「子」オブジェクトクラス (のコンストラクタ) を作成する方法を示します。また OOJS をいつどこで使用するかについてのアドバイスも提供しています。
+
JSON データの操作
+
JavaScript Object Notation (JSON) は、JavaScript オブジェクト構文に基づいて構造化されたデータを表現するための標準のテキストベースのフォーマットであり、ウェブサイト上でデータを表現し、送信するために一般的に使用されます (すなわち、サーバーからクライアントへデータを送信して、その結果ウェブページ上に表示することができます)。これはとても良く見るものなので、この記事では、JSON を解析してその中のデータ項目にアクセスしたり、独自の JSON を記述したりするなど、JavaScript を使用して JSON を操作するために必要なことをすべて説明します。
+
オブジェクト構築の練習
+
これまでの記事では、基本的な JavaScript オブジェクトの理論と構文の詳細をすべて見てもらい、あなたには出発できるしっかりとした基本が与えられました。この記事では実践的なエクササイズを行い、もっと楽しい色とりどりの色付きのボールを作成するカスタム JavaScript オブジェクトを作る練習をしていきます。
+
+ +

評価

+ +
+
バウンスボールのデモへの機能の追加
+
この評価では、前の記事のバウンスボールデモを出発点として使用し、新しい興味深い機能を追加する予定です。
+
diff --git a/files/ja/learn/javascript/objects/inheritance/index.html b/files/ja/learn/javascript/objects/inheritance/index.html new file mode 100644 index 0000000000..7830f5a676 --- /dev/null +++ b/files/ja/learn/javascript/objects/inheritance/index.html @@ -0,0 +1,412 @@ +--- +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 でどのようにするのでしょうか。

+ +

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

+ +

さあ始めてみよう

+ +

まず、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/json/index.html b/files/ja/learn/javascript/objects/json/index.html new file mode 100644 index 0000000000..c72a38744b --- /dev/null +++ b/files/ja/learn/javascript/objects/json/index.html @@ -0,0 +1,357 @@ +--- +title: JSON データの操作 +slug: Learn/JavaScript/Objects/JSON +tags: + - Article + - Beginner + - CodingScripting + - Guide + - JSON + - JavaScript + - Learn + - Objects + - Tutorial + - 'l10n:priority' +translation_of: Learn/JavaScript/Objects/JSON +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects/Object_building_practice", "Learn/JavaScript/Objects")}}
+ +

JavaScript Object Notation (JSON) は表現用の標準的なテキストベースの構造データ表現フォーマットで、JavaScript 構造データオブジェクトの表記法をベースとしています。一般的にはウェブアプリケーションでデータを転送する場合に使われます。(例えば、データをサーバーからクライアントへ送信する場合などで、ウェブページに表示されたりすることもあり、その逆もあります)。頻繁に見かけるデータフォーマットですので、この節では JavaScript を使って JSON をパースする、JSON のデータを参照する、JSON を作るなど、JSON を扱うために必要となる操作を説明します。

+ + + + + + + + + + + + +
前提条件:基礎的なコンピュータの知識、HTML と CSS への基本的な理解、基礎的な JavaScript の理解 (JavaScript の第一歩JavaScript の構成要素を参照) とオブジェクト指向JavaScript の基本 (JavaScript オブジェクトの基本を参照)。
目的:JSON内のデータの扱い方、JSON 文字列の作成方法について理解できること。
+ +

JSON とは何か

+ +

{{glossary("JSON")}} は JavaScript オブジェクトの構文に従ったテキストベースのフォーマットで、Douglas Crockford によって普及されました。JSON は JavaScript オブジェクトの構文に似ていますが、JavaScript とは独立して扱われることがあり、多くのプログラミング言語環境には JSON を読み込む(パースする)したり生成したりする機能があります。

+ +

JSON は文字列です。ですので、ネットワークを通してデータを転送したい場合に便利です。JSON データへアクセスしたい場合は、JavaScript オブジェクトへ変換する必要があります。JavaScript には JSON と JavaScript オブジェクトを相互に変換できるメソッドを持った JSON というグローバルなオブジェクトがあるので、その変換は大きな問題ではありません。

+ +
+

注記: 文字列をネイティブオブジェクトへ変換することはデシリアライゼーション (deserialization) と呼ばれており、ネイティブオブジェクトをネットワークを通して転送できように文字列へ変換することはシリアライゼーション (serialization) と呼ばれています。

+
+ +

JSON 文字列はそれ自身をファイルとして格納することもできます。それは {{glossary("MIME type")}} が application/json で、.json という拡張子の付いたただのテキストファイルです。

+ +

JSON の構造

+ +

上で説明したように、JSON は JavaScript オブジェクトにとても似ているフォーマットを持った文字列です。JSON では通常の JavaScript オブジェクトと同様な基本データ型(文字列、数値、配列、ブーリアンやその他のリテラル型)を使うことができます。これにより、以下のように階層的にデータを構成することができます。

+ +
'{
+  "squadName": "Super hero squad",
+  "homeTown": "Metro City",
+  "formed": 2016,
+  "secretBase": "Super tower",
+  "active": true,
+  "members": [
+    {
+      "name": "Molecule Man",
+      "age": 29,
+      "secretIdentity": "Dan Jukes",
+      "powers": [
+        "Radiation resistance",
+        "Turning tiny",
+        "Radiation blast"
+      ]
+    },
+    {
+      "name": "Madame Uppercut",
+      "age": 39,
+      "secretIdentity": "Jane Wilson",
+      "powers": [
+        "Million tonne punch",
+        "Damage resistance",
+        "Superhuman reflexes"
+      ]
+    },
+    {
+      "name": "Eternal Flame",
+      "age": 1000000,
+      "secretIdentity": "Unknown",
+      "powers": [
+        "Immortality",
+        "Heat Immunity",
+        "Inferno",
+        "Teleportation",
+        "Interdimensional travel"
+      ]
+    }
+  ]
+}'
+ +

もし、この文字列を JavaScript プログラムへ読み込んだ場合(例えば、例えば変数superHeroes へ代入する)、JavaScript オブジェクトの基本の節で見たのと同様に ドットや角括弧を使ってデータへアクセスすることができます。例としては以下のようになります。

+ +
superHeroes.homeTown
+superHeroes['active']
+ +

さらに深い階層のデータへアクセスする場合は、単純にプロパティ名や配列のインデックスを連結します。例えば、メンバーリスト中2番目のヒーローの 3番目の能力を参照する場合は、以下のようになります。

+ +
superHeroes['members'][1]['powers'][2]
+ +
    +
  1. まず、変数名superHeroes を指定します。
  2. +
  3. その中の members プロパティへアクセスしたいので、["members"]と指定します。
  4. +
  5. members にはオブジェクトの配列が格納されています. ここでは、配列内の 2番目のオブジェクトへアクセスするので、[1]を指定します。
  6. +
  7. そのオブジェクト内で、powers プロパティへアクセスするため, ["powers"]と指定します。
  8. +
  9. powers プロパティは選択したヒーローの能力を含んだ配列となっており、その中の 3番目が欲しいので、[2]と記述します。
  10. +
+ +
+

注記: 上記の JSON は JSONTest.html で参照することができます。(ページ内の source code を参照してください)。ページを読み込んで見て、ブラウザーのコンソールで変数内のデータにアクセスしてみてください。

+
+ +

JSON 配列

+ +

上記で、JSON テキストは基本的に文字列に入った JavaScript オブジェクトのように見えることを説明しました。配列を JSON との間で変換することもできます。以下も有効な JSON です。例:

+ +
'[
+  {
+    "name": "Molecule Man",
+    "age": 29,
+    "secretIdentity": "Dan Jukes",
+    "powers": [
+      "Radiation resistance",
+      "Turning tiny",
+      "Radiation blast"
+    ]
+  },
+  {
+    "name": "Madame Uppercut",
+    "age": 39,
+    "secretIdentity": "Jane Wilson",
+    "powers": [
+      "Million tonne punch",
+      "Damage resistance",
+      "Superhuman reflexes"
+    ]
+  }
+]'
+ +

これも有効な JSON であり、パースしたデータには配列のインデックスを指定するだけです。例えば、[0]["powers"][0]のように表記できます。

+ +

その他の注意点

+ + + +

手を動かして学ぼう: JSON をさわってみる

+ +

それでは、Web サイト上でどのように JSON 形式のデータを使うことができるか例を通して見てみましょう。

+ +

はじめに

+ +

まず、heroes.html と style.css のコピーをローカルに作成してください。後者は例題ページをスタイリングするための CSS であり、前者は簡単な形式の HTML です。

+ +
<header>
+</header>
+
+<section>
+</section>
+ +

他には、この演習で書く JavaScript を含んだ {{HTMLElement("script")}} 要素があります。この時点では、{{HTMLElement("header")}} 要素と {{HTMLElement("section")}} 要素 を取得して、変数へ代入している 2行コードのみが書かれています。

+ +
const header = document.querySelector('header');
+const section = document.querySelector('section');
+ +

演習用の JSON データは https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json に用意してあります。

+ +

そのデータを演習ページに読み込んで、それを表示するのにいくらかの DOM操作を行います。最終的には、以下の画像のようになります。

+ +

+ +

JSON の取得

+ +

JSON を取得するには、{{domxref("XMLHttpRequest")}} (しばしば XHR と呼ばれる) という API を使用します。これは非常に便利な JavaScript オブジェクトで、JavaScript を使用してサーバからリソース (例:画像、テキスト、JSON、さらには HTML スニペットなど) を取得するネットワークリクエストを行うことができます。つまりページ全体を再読み込みせずに、小さな部分のコンテンツを更新することができます。これにより、よりレスポンシブな Web ページを作成できますが、それをもっと詳細に教えるのはこの記事の範囲を超えています。

+ +
    +
  1. まず、取得したい JSON がある URL を変数へ代入します。次のコードを JavaScript の最後の行へ追加してください。 +
    let requestURL = 'https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json';
    +
  2. +
  3. HTTP リクエストを作成するのに、new を使って XMLHttpRequest から新しいリクエストオブジェクトをつくる必要があります。先ほどのコードの下に、次のコードを追加してください。 +
    let request = new XMLHttpRequest();
    +
  4. +
  5. 新しいリクエストを開始するのには open() メソッドを使います。 次のコードを追加してください。 +
    request.open('GET', requestURL);
    + +

    このメソッドは最低2つのパラメータを引数として取ります(他に任意の引数を与えることもできます)。今回の簡単な例では、次の2つの必須パラメータのみを利用します。

    + +
      +
    • リクエストを開始する際に HTTP のメソッドを決める必要があります。今回のケースでは、単純にデータを取得するだけですので GET が良いでしょう。
    • +
    • リクエストを送る先の URL。今回は JSON ファイルが置かれている URL です。
    • +
    +
  6. +
  7. 次に、以下の2行のコードを追加してください。XHR オブジェクトがサーバーから返されるデータを判断できるように responseType に JSON を指定します。すると、ブラウザ側で JavaScript オブジェクトへ変換してくれます。それから、send() メソッドでリクエストを送信します。 +
    request.responseType = 'json';
    +request.send();
    +
  8. +
  9. 最後に、サーバーからのレスポンスを待ち、それを処理するコードを用意するので、以下のコードをこれまでのコードの下に追加してください。 +
    request.onload = function() {
    +  const superHeroes = request.response;
    +  populateHeader(superHeroes);
    +  showHeroes(superHeroes);
    +}
    +
  10. +
+ +

ここでは、先ほどのリクエストに対するレスポンス (response プロパティから取得できます) を superHeroes という変数へ代入しています。つまり、この変数に JSON を元に生成された JavaScript オブジェクトが格納されているということです! それから 2 つの関数をそのオブジェクトを引数として与えて呼び出しています。最初の関数は引数のデータを <header> へ埋め込み、2 つ目は各ヒーローごとのインフォメーションカードを作り、<section> へ埋め込みます。

+ +

上記の処理は、リクエストオブジェクトで load イベントが発生した時に呼び出される関数 (onload を参照) に記述しました。このイベントはレスポンスがうまく取得できた場合に呼び出されるので、 request.response を使って何か処理をしようとしたときに、それが必ず利用できることが保証されています。

+ +

ヘッダーへの値のセット

+ +

ここまでで、JSON の取得と JavaScript オブジェクトへの変換ができました、先ほどの 2 つの関数を実装して使ってみましょう。まずはじめに、以下のコードをこれまでのコードの下に追加してください。

+ +
function populateHeader(obj) {
+  const myH1 = document.createElement('h1');
+  myH1.textContent = obj['squadName'];
+  header.appendChild(myH1);
+
+  const myPara = document.createElement('p');
+  myPara.textContent = 'Hometown: ' + obj['homeTown'] + ' // Formed: ' + obj['formed'];
+  header.appendChild(myPara);
+}
+ +

まず、createElement() で {{HTMLElement("h1")}} 要素を生成、その textContent プロパティにそのオブジェクトの squadName プロパティをセット、そしてそれを appendChild() を使いヘッダーに追加します。そして要素の生成、テキストのセット、ヘッダーへの追加という同じような操作をパラグラフ要素でも行います。セットするテキストの値が homeTownformed プロパティの文字列を結合したものであるという点だけが異なります。

+ +

ヒーローインフォメーションカードの作成

+ +

次に、以下の関数をコードの下へ追記してください。この関数はスーパーヒーローカードの作成と画面表示を行います。

+ +
function showHeroes(jsonObj) {
+  const heroes = jsonObj['members'];
+
+  for (let i = 0; i < heroes.length; i++) {
+    const myArticle = document.createElement('article');
+    const myH2 = document.createElement('h2');
+    const myPara1 = document.createElement('p');
+    const myPara2 = document.createElement('p');
+    const myPara3 = document.createElement('p');
+    const myList = document.createElement('ul');
+
+    myH2.textContent = heroes[i].name;
+    myPara1.textContent = 'Secret identity: ' + heroes[i].secretIdentity;
+    myPara2.textContent = 'Age: ' + heroes[i].age;
+    myPara3.textContent = 'Superpowers:';
+
+    const superPowers = heroes[i].powers;
+    for (let j = 0; j < superPowers.length; j++) {
+      const listItem = document.createElement('li');
+      listItem.textContent = superPowers[j];
+      myList.appendChild(listItem);
+    }
+
+    myArticle.appendChild(myH2);
+    myArticle.appendChild(myPara1);
+    myArticle.appendChild(myPara2);
+    myArticle.appendChild(myPara3);
+    myArticle.appendChild(myList);
+
+    section.appendChild(myArticle);
+  }
+}
+ +

始めに、JavaScript オブジェクトの members プロパティを新しい変数に保存します。
+ この配列は複数のオブジェクトを持ち、オブジェクトはそれぞれのヒーローについての情報を持ちます。

+ +

次に、for ループを使って配列の個々のオブジェクトについてループしていきます。それぞれについて:

+ +
    +
  1. 新しい要素をいくつか作ります: <article> 1つ、<h2> 1つ、3つの <p> と1つの <ul> です。
  2. +
  3. <h2> の中身を今のヒーローの名前 (name) にします。
  4. +
  5. 3つの <p> の中身を、それぞれの secretIdentityage、リストにある情報を紹介していくために「超能力 ("Superpowers:")」で始まる行とします。
  6. +
  7. powers プロパティを superPowers という新しい定数に保存します — この定数は今のヒーローの超能力のリストを持つ配列です。
  8. +
  9. 別の for ループをつかって、今のヒーローの超能力をループします — それぞれに対する <li> 要素を作成し、超能力をこの要素の中身とし、<ul>要素(myList変数)の listItemappendChild() を使って追加します。
  10. +
  11. 最後の最後にやるのは、<h2><p><ul><article> (myArticle) の中身に追加し、それから <article><section> の中身に追加します。HTML の中身として表示される順序になりますので、これらの要素が追加された順序は重要です。
  12. +
+ +
+

付記: 例を動かしてみるのに問題があったら、heroes-finished.html ソースコードを参照して見て下さい(こちらで ライブ実行 もできます)。

+
+ +
+

付記: もし JavaScript オブジェクトにアクセスするのに使っているドット/ブラケット記法をなぞっていくのが難しければ、superheroes.json  のファイルを別のタブやテキストエディタで開いておいて、JavaScript と並べて読んでいくとわかりやすいかもしれません。JavaScript オブジェクトの基本 記事にも戻って、ドット/ブラケット記法について読み返してみてください。

+
+ +

オブジェクトとテキスト間の変換

+ +

上の例は XHR リクエストで JSON レスポンスを直接JavaScript オブジェクトに変換していたので、JavaScript オブジェクトへのアクセスという面では単純でした。次の部分です:

+ +
request.responseType = 'json';
+ +

時にはこんなにツイていない場合もあります — 時には生の  JSON 文字列を受けとり、自分でオブジェクトに変換しなければならない場合もあるでしょう。また JavaScript オブジェクトをネットワーク越しに送信したい場合、送信する前に JSON 文字列に変換しなければならないでしょう。ツイている事に、ウェブ開発でこの二つの問題にはしょっちゅう出くわすので、ブラウザには組込みの JSON  オブジェクトが備わっていて、これは以下二つのメソッドを備えています:

+ + + +

一つめの方の動作例が heroes-finished-json-parse.html にあります (ソース を見て下さい) — ここでは前の方で作成した例と全く同じ事をしていますが、XHR では生の JSON 文字列を返させて、それを parse() で JavaScript オブジェクトに変換しているところだけが異なります。コードの重要な箇所はこの部分です:

+ +
request.open('GET', requestURL);
+request.responseType = 'text'; // now we're getting a string!
+request.send();
+
+request.onload = function() {
+  const superHeroesText = request.response; // get the string from the response
+  const superHeroes = JSON.parse(superHeroesText); // convert it to an object
+  populateHeader(superHeroes);
+  showHeroes(superHeroes);
+}
+ +

で、ご想像の通り stringify()  は全く反対の向きに動作します。次の行をブラウザーの JavaScript コンソールに一つずつ打ち込んでいって、実際に動かしてみて下さい:

+ +
let myObj = { name: "Chris", age: 38 };
+myObj
+let myString = JSON.stringify(myObj);
+myString
+ +

JavaScript オブジェクトを作成してその中身を確認し、次に stringify() を使って JSON 文字列に変換し — 戻り値を新しい変数に保存しています — その値も確認しています。

+ +

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

+ +

この記事はここまでですが、最も重要な情報を覚えていますか?先に進む前に、この情報を保持しているかどうかを確認するためのテストがいくつかあります — Test your skills: JSON を参照してください。

+ +

まとめ

+ +

この節では、プログラム内で、JSON を生成する、JSON をパースする、JSON データを参照するなど、JSON を扱う方法について簡単に説明しました。次の節では、オブジェクト指向 JavaScript について見ていくことにします。

+ +

あわせて参照

+ + + +

{{PreviousMenuNext("Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects/Object_building_practice", "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 new file mode 100644 index 0000000000..ab99282acb --- /dev/null +++ b/files/ja/learn/javascript/objects/object-oriented_js/index.html @@ -0,0 +1,291 @@ +--- +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")}}

+ +

このモジュール内の文書

+ + diff --git a/files/ja/learn/javascript/objects/object_building_practice/index.html b/files/ja/learn/javascript/objects/object_building_practice/index.html new file mode 100644 index 0000000000..af94a8eede --- /dev/null +++ b/files/ja/learn/javascript/objects/object_building_practice/index.html @@ -0,0 +1,314 @@ +--- +title: オブジェクト構築の練習 +slug: Learn/JavaScript/Objects/Object_building_practice +tags: + - Article + - Beginner + - Canvas + - CodingScripting + - Guide + - JavaScript + - Learn + - Objects + - Tutorial + - 'l10n:priority' +translation_of: Learn/JavaScript/Objects/Object_building_practice +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects/Adding_bouncing_balls_features", "Learn/JavaScript/Objects")}}
+ +

ここまでの記事で JavaScript オブジェクトの根幹部に関する理論と文法の詳細についてすべてを見てきて、始めていくのに十分な基礎固めをしました。この記事では実練習を行ない、独自の JavaScript オブジェクトを作っていくための実践をしていきましょう — 楽しくてカラフルなものを。

+ + + + + + + + + + + + +
前提条件:基礎的なコンピューターの知識、HTML と CSS への基本的な理解、基礎的な JavaScript の理解 (JavaScript の第一歩JavaScript の構成要素を参照) とオブジェクト指向JavaScript の基本 (JavaScript オブジェクトの基本を参照)。
目的:オブジェクトの使い方とオブジェクト指向のテクニックを実世界のコンテストで練習する。
+ +

ボールを弾ませよう

+ +

この記事では伝統的な「弾むボール」のデモを作ってみて、JavaScript でどれほどオブジェクトが役に立つかお見せしましょう。小さなボールは画面じゅうを跳ねまわり、それぞれがぶつかると色が変わります。完成したものはこんな風に見えることでしょう:

+ +

+ +
    +
+ +

この例では画面にボールを描くのに Canvas API を使い、画面をアニメーションさせるのに requestAnimationFrame を使います — これらの API について事前の知識は不要です。この記事を読み終わる頃にはこれら API についてもっと知りたくなっているだろうと期待してますが。道中では、イカしたオブジェクトを活用して、ボールを壁で弾ませる、それぞれがぶつかった事を判定する(衝突判定という呼び名で知られています)といった上手いテクニックをいくつかお見せしていきます。

+ +

始めに

+ +

始める前に index.html, style.css, と main.js ファイルのローカルコピーを作成してください。これらにはそれぞれ、以下が含まれています:

+ +
    +
  1. とても簡素な HTML文書で、{{HTMLElement("h1")}} 要素と、ボールを描画するための {{HTMLElement("canvas")}} 要素と、この HTML に CSS と JavaScript を適用するための要素だけからなります。
  2. +
  3. とても簡単なスタイル、主には<h1>のスタイルとポジションを指定し、スクロールバーやページ端周辺のマージンを消す(素敵にきれいに見せるため)ためのもの。
  4. +
  5. <canvas>要素を設定し、これから使うことになる汎用の関数を提供する若干の JavaScript。
  6. +
+ +

スクリプトの最初の部分はこんな具合です:

+ +
const canvas = document.querySelector('canvas');
+
+const ctx = canvas.getContext('2d');
+
+const width = canvas.width = window.innerWidth;
+const height = canvas.height = window.innerHeight;
+ +

このスクリプトでは<canvas>要素への参照を取得し、これに対して getContext() メソッドを使って描画していくためのコンテキストを取得します。得られる定数 (ctx) はキャンバスの描画可能領域を直接表現しており、ここに二次元の形状を書き込む事ができます。

+ +

次に widthheight 二つの定数をセットし、キャンバス要素の幅と高さ(canvas.widthcanvas.height プロパティで表わされます)をブラウザーのビューポートの幅と高さ(ウェブページが表示される領域です — {{domxref("Window.innerWidth")}} と{{domxref("Window.innerHeight")}} プロパティから取得できます)に等しくします。

+ +

変数値をさっと全部同じにするのに、代入が連鎖している事に注意してください — これで全く問題ありません。

+ +

初期化スクリプトの最後の部分はこんなのです:

+ +
function random(min, max) {
+  const num = Math.floor(Math.random() * (max - min + 1)) + min;
+  return num;
+}
+ +

この関数は二つの数を引数に取り、二つ数の範囲内の乱数を戻します。

+ +

我々のプログラム用のボールを一つモデル化する

+ +

我々のプログラムでは画面中を跳ねまわるたくさんのボールがあります。これらのボールはどれも同じルールで動くので、1つのオブジェクトで表わすのが理に叶っています。まずはコードの最後に以下のコンストラクターを追加するところから始めましょう。

+ +
function Ball(x, y, velX, velY, color, size) {
+  this.x = x;
+  this.y = y;
+  this.velX = velX;
+  this.velY = velY;
+  this.color = color;
+  this.size = size;
+}
+ +

ここではいくつかの引数を用意し、我々のプログラムの中で個々のボールが動作するのに必要なプロパティを定義しています:

+ + + +

これはプロパティを取り扱いましたが、メソッドはどうしましょう? プログラムの中ではボールに実際に何かさせたいわけです。

+ +

ボールを描画する

+ +

まず以下の draw() メソッドを Ball() のプロトタイプ(prototype)に追加しましょう:

+ +
Ball.prototype.draw = function() {
+  ctx.beginPath();
+  ctx.fillStyle = this.color;
+  ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
+  ctx.fill();
+}
+ +

この関数を使って、以前定義した 2D キャンバスコンテキスト(ctx)のメンバーを順に呼び出す方法で、ボール自身が画面に自分を描画する方法を教え込みます。コンテキストは紙のようなもので、ペンを使って何か描くように指示したいわけです:

+ + + +

これでオブジェクトをテストしてみられるようになりました。

+ +
    +
  1. コードを保存し、HTML ファイルをブラウザーで読み込みます。
  2. +
  3. ブラウザーの JavaScript コンソールを開いて、ページをリフレッシュし、キャンバスのサイズがコンソール分小さくなったビューポート領域に合うようにします。
  4. +
  5. 次をタイプして、新しいボールのインスタンスを作成します: +
    let testBall = new Ball(50, 100, 4, 4, 'blue', 10);
    +
  6. +
  7. そのメンバを呼び出して見てください: +
    testBall.x
    +testBall.size
    +testBall.color
    +testBall.draw()
    +
  8. +
  9. 最後の行を入力すると、キャンバスのどこかにボールが表示されたはずです。
  10. +
+ +

ボールのデータを更新する

+ +

ボールを座標に表示できるようになりましたが、ボールを実際に移動させるには、何らかの更新するための関数が必要です。JavaScript ファイルの最後に以下のコードを追加し、update() メソッドを Ball()prototype に追加します:

+ +
Ball.prototype.update = function() {
+  if ((this.x + this.size) >= width) {
+    this.velX = -(this.velX);
+  }
+
+  if ((this.x - this.size) <= 0) {
+    this.velX = -(this.velX);
+  }
+
+  if ((this.y + this.size) >= height) {
+    this.velY = -(this.velY);
+  }
+
+  if ((this.y - this.size) <= 0) {
+    this.velY = -(this.velY);
+  }
+
+  this.x += this.velX;
+  this.y += this.velY;
+}
+ +

関数の頭から 4 つの部分でボールがキャンバスの端に達したかどうかチェックします。もしそうであれば、関連する速度の向きを反転してボールが反対の向きに移動するようにします。つまり例えば、ボールが上方向に移動していたならば(velY が正)、垂直方向の速度をボールが下方向に移動するように変更します(velY を負に)。(訳注: 左上が原点、右下が座標の正方向ならば、ボールが上に移動する時の velY は負のはずだけど…)

+ +

4 つの場合で、次のことを確認しています:

+ + + +

それぞれの場合で計算にボールの size を含めていますが、これは x/y座標はボールの中心ですが、ボールの端のところで周囲から跳ね返って欲しいからです — 跳ね返る前に画面外にめり込んで欲しくないからです。

+ +

最後の二行では velXx 座標に、velYy 座標に加算しています — 結果ボールはこのメソッドが呼ばれる毎に移動します。

+ +

とりあえずはここまでで、ちょいとアニメーションさせてみよう!

+ +

ボールのアニメーション

+ +

さあ、楽しい事をやりましょう。では、キャンバスにボールを追加し、アニメーションさせるところから始めましょう。

+ +
    +
  1. 最初に、ボールを全部保存しておく場所がどこかに必要です。以下がこれをやってくれます — あなたのコードの最後に追加してください: +
    let balls = [];
    +
    +while (balls.length < 25) {
    +  let size = random(10,20);
    +  let ball = new Ball(
    +    // ball position always drawn at least one ball width
    +    // away from the edge of the canvas, to avoid drawing errors
    +    random(0 + size,width - size),
    +    random(0 + size,height - size),
    +    random(-7,7),
    +    random(-7,7),
    +    'rgb(' + random(0,255) + ',' + random(0,255) + ',' + random(0,255) +')',
    +    size
    +  );
    +
    +  balls.push(ball);
    +}
    + +

    while ループは、我々の random()関数で作成したランダムな値を使った新しい Ball() のインスタンスを作成し、ボールの配列の後ろに push() して追加していきますが、これは配列中のボールの数が 25 に満たない間まで繰り返されます。balls.length < 25 の数字をいろいろ変えれば表示されるボールの数を増やしたり減らしたりできます。あなたのコンピューターとブラウザーがどれだけ速いかによりますが、ボールを数千にするとアニメーションはかなり遅くなります! 注意してね。

    +
  2. +
  3. 以下をあなたのコードの末尾に追加してください: +
    function loop() {
    +  ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
    +  ctx.fillRect(0, 0, width, height);
    +
    +  for (let i = 0; i < balls.length; i++) {
    +    balls[i].draw();
    +    balls[i].update();
    +  }
    +
    +  requestAnimationFrame(loop);
    +}
    + +

    ものをアニメーションさせるすべてのプログラムには、大概アニメーションループがあり、プログラム内の情報を更新して、アニメーションの各フレームでその結果を表示します。これは大半のゲームや類似するプログラムの基本になります。コード中の loop() 関数は以下の事を行ないます:

    + +
      +
    • キャンバスの塗り潰し色を半透明の黒にし、その色でキャンバスの幅と高さいっぱいの長方形を fillRect() で描きます(これの 4 つの引数は始点の座標と、描画する長方形の幅と高さになります)。これで次のフレームを描く前に、前のフレームで描いた内容を見えなくします。これをしないと、ボールじゃなくて長い蛇がキャンバスの中を這い回る様を見る事になります! 塗り潰す色は半透明の rgba(0,0,0,0.25) なので、以前の何フレーム分かがかすかに残り、ボールが移動した後の軌跡を表現します。もし 0.25 を 1 に変更すると、軌跡は全く見えなくなります。この値を変えて、どんな効果になるか見てみてください。
    • +
    • ループで balls配列のボール全部をなめてそれぞれのボールの draw()update() 関数を実行し、それぞれを画面に描画してから、次のフレームに備えて必要な位置と速度の更新を行います。
    • +
    • この関数を requestAnimationFrame() メソッドを使って再実行します — このメソッドが繰り返し実行され同じ関数名を与えられると、その関数がスムースなアニメーションを行なうために毎秒設定された回数実行されます。これはたいてい再帰的に行われます — つまり関数は毎回その関数自身を呼び出すので、何度も何度も繰り返し実行されます。
    • +
    +
  4. +
  5. 最後に、あなたのコードの最後に次の行を追加します — アニメーションを開始するために、一旦は関数を呼ぶ必要があるのです。 +
    loop();
    +
  6. +
+ +

基本としてはこんなところ — セーブしてリフレッシュして、ボールがはずむのをテストしてみてください!

+ +

衝突判定を追加する

+ +

さあ、もうちょっと面白くするため、プログラムに衝突判定を追加して、ボールに他のボールとぶつかったらどうするのか教えましょう。

+ +
    +
  1. 最初に、以下のメソッド定義を update() メソッドを定義した箇所(つまり Ball.prototype.update ブロック)の下に追加します + +
    Ball.prototype.collisionDetect = function() {
    +  for (let j = 0; j < balls.length; j++) {
    +    if (!(this === balls[j])) {
    +      const dx = this.x - balls[j].x;
    +      const dy = this.y - balls[j].y;
    +      const distance = Math.sqrt(dx * dx + dy * dy);
    +
    +      if (distance < this.size + balls[j].size) {
    +        balls[j].color = this.color = 'rgb(' + random(0, 255) + ',' + random(0, 255) + ',' + random(0, 255) +')';
    +      }
    +    }
    +  }
    +}
    +
  2. +
  3. +

    このメソッドはちょっとばかり複雑なので、今はどんな動作をしているのか正確に理解できなくても構いません。説明していきます:

    + +
      +
    • それぞれのボールで、他のボールそれぞれとこのボールが衝突していないか調べなければなりません。そのために、balls[]配列すべてのボールを回すために別の for ループを始めます。
    • +
    • 内側のループに入ってすぐ、if文でループで回しているボールがチェックしているボールと同じか調べています。ボールがそれ自体とぶつかっているかチェックしたくないですから! これのために、現在のボール(collisionDetect メソッドが実行されているボールです)がループ中のボール(現在の collisionDetect メソッド内のループのくりかえし中で参照されているボール)と一致しているかチェックします。!を使って等価性チェックを逆にしているので、if 文の中のコードはボールが同じでないときだけ実行されます。
    • +
    • そして二つの円が衝突していないか調べるための一般的なアルゴリズムを使っています。基本的には円ないの領域が重なっているかチェックしています。これについて詳しくは 2次元の衝突判定で解説されています。
    • +
    • もし衝突が検出されたら、内側の if文の中のコードが実行されます。この場合では、両方のボールの color プロパティをランダムな新しい色に設定しているだけです。もっと複雑なこと、現実っぽくボールを互いに跳ね返らせたりもできたでしょうが、これを実装したとするともっとずっとに複雑なったでしょう。そのような物理シミュレーションには、PhysicsJS, matter.js, Phaser などのゲームや物理用のライブラリを使う開発者が多いです。
    • +
    +
  4. +
  5. あなたはアニメーションの各フレーム毎にこのメソッドを呼ばなければなりません。以下を balls[i].update(); の行の後に追加してください: +
    balls[i].collisionDetect();
    +
  6. +
  7. 保存とデモのリフレッシュをして、ボールがぶつかった時に色が変わるのを見てください!
  8. +
+ +
+

注記: この例題を動かすのに困った時は、あなたの JavaScript コードを私たちの完成版と比べてみてください(ライブ実行版も見られます)。

+
+ +

まとめ

+ +

自分版の実世界で跳ね回るランダムボール例作り、この全単元で出てきた様々なオブジェクトやオブジェクト指向テクニックを使ったものをあなたに楽しんでいただけていれば、と思います。オブジェクトの実践的な使い方の練習や、実世界のコンテキストについて得られるものがあったはずです。

+ +

オブジェクトに関する記事は以上です — 残るのは、あなが自分のスキルをオブジェクトの評価問題で試してみる事だけです。

+ +

参考文献

+ + + +

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

+ +

このモジュール

+ + diff --git a/files/ja/learn/javascript/objects/object_prototypes/index.html b/files/ja/learn/javascript/objects/object_prototypes/index.html new file mode 100644 index 0000000000..4e9419e49d --- /dev/null +++ b/files/ja/learn/javascript/objects/object_prototypes/index.html @@ -0,0 +1,314 @@ +--- +title: Object のプロトタイプ +slug: Learn/JavaScript/Objects/Object_prototypes +tags: + - Beginner + - CodingScripting + - JavaScript + - Learn + - OOJS + - OOP + - Object + - Prototype + - create() + - 'l10n:priority' + - コンストラクタ + - 記事 +translation_of: Learn/JavaScript/Objects/Object_prototypes +--- +
{{LearnSidebar}}
+ +
{{PreviousMenuNext("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects")}}
+ +

プロトタイプは、JavaScript オブジェクトが互いに機能を継承するメカニズムです。この記事では、プロトタイプチェーンの仕組みを説明し、prototype プロパティを使って既存のコンストラクタにメソッドを追加する方法を見ていきます。

+ +
+

Note: この記事では、伝統的な JavaScript のコンストラクタとクラスを取り上げます。次の記事では、同じことを実現するためのより簡単な構文を提供する現代的な方法について話します - ECMAScript 2015 のクラスを参照してください。

+
+ + + + + + + + + + + + +
前提条件:JavaScript 関数の理解、JavaScript の基礎知識 (JavaScript の第一歩JavaScript の構成要素を参照)、OOJS の基礎 (JavaScript オブジェクトの基本を参照)
目的:JavaScript のオブジェクトのプロトタイプ、プロトタイプチェーンの動作方法、prototype プロパティに新しいメソッドを追加する方法を理解する。
+ +

プロトタイプベースの言語とは?

+ +

JavaScript はしばしばプロトタイプベースの言語として記述されます - 継承を提供するために、オブジェクトはメソッドやプロパティを継承するテンプレートオブジェクトとして機能する prototype オブジェクトを持つことができます。

+ +

オブジェクトのプロトタイプオブジェクトは、メソッドやプロパティを継承するプロトタイプオブジェクトを持つことができます。これはしばしばプロトタイプチェーンと呼ばれ、異なるオブジェクトが他のオブジェクトに定義されたプロパティやメソッドを持つ理由を説明しています。

+ +

JavaScript では、オブジェクトのインスタンスとプロトタイプ (コンストラクタの prototype プロパティから派生した __proto__ プロパティ) の間にリンクが張られており、プロパティとメソッドはプロトタイプの連鎖を辿って見つけられます。

+ +
+

Note: オブジェクトの prototype (Object.getPrototypeOf(obj) または非推奨の __proto__ プロパティで取得可能) とコンストラクタ関数のprototype プロパティの違いを理解することが重要です。

+ +

前者は各インスタンス上のプロパティ、後者はコンストラクタ上のプロパティです。つまり、Object.getPrototypeOf(new Foobar())Foobar.prototypeと同じオブジェクトを参照しています。

+
+ +

これを少し明確にするための例を見てみましょう。

+ +

プロトタイプオブジェクトの理解

+ +

ここでは、Person() コンストラクタを書き終えた例に戻ります - ブラウザで例を読み込んでください。前回の記事で紹介した oojs-class-further-exercises.html の例を使うことができます (ソースコードも参照してください)。

+ +

この例では、次のようにコンストラクタ関数を定義しています。

+ +
function Person(first, last, age, gender, interests) {
+
+  // property and method definitions
+  this.name = {
+    'first': first,
+    'last' : last
+  };
+  this.age = age;
+  this.gender = gender;
+  //...see link in summary above for full definition
+}
+ +

次に、このようなオブジェクトインスタンスを作成します。

+ +
let person1 = new Person('Bob', 'Smith', 32, 'male', ['music', 'skiing']);
+ +

JavaScript コンソールに "person1." と入力すると、ブラウザがこのオブジェクトで利用可能なメンバ名でこれを自動補完しようとするはずです:

+ +

+ +

このリストでは、person1 のコンストラクタである Person() で定義されているメンバ - nameagegenderinterestsbiogreeting - が表示されています。しかし、他にも toStringvalueOf などのメンバがあり、これらのメンバは person1 の prototype オブジェクトの prototype オブジェクト (Object.prototype) で定義されています。

+ +

+ +

実際に Object.prototype で定義されている person1 のメソッドを呼び出すとどうなりますか?例えば

+ +
person1.valueOf()
+ +

valueOf() は、呼び出されたオブジェクトの値を返します。この場合、何が起こるかというと

+ + + +
+

Note: プロトタイプチェーンの中では、メソッドやプロパティはあるオブジェクトから別のオブジェクトにコピーされないことを再確認しておきましょう。これらのメソッドやプロパティは、上で説明したようにチェーンを上っていくことでアクセスされます。

+
+ +
+

Note: プロトタイプチェーンは、プロパティを取得している間のみ巡回されます。プロパティがオブジェクトに直接設定されたり削除されたりした場合は、プロトタイプチェーンは走査されません。

+
+ +
+

Note: ECMAScript 2015 以前は、オブジェクトの prototype に直接アクセスする方法は公式にはありませんでした - チェーン内のアイテム間の「リンク」は、JavaScript 言語の仕様で [[prototype]] と呼ばれる内部プロパティで定義されています ({{glossary("ECMAScript")}}}を参照してください)。

+ +

しかし、ほとんどの最新のブラウザでは、オブジェクトのコンストラクタのプロトタイプオブジェクトを含む __proto__ (アンダースコア2個分) というプロパティを提供しています。例えば、person1.__proto__person1.__proto__.__proto__ を試してみてください。

+ +

ECMAScript 2015 からは、Object.getPrototypeOf(obj) を介して間接的にオブジェクトのプロトタイプオブジェクトにアクセスすることができます。

+
+ +

prototypeプロパティ:継承されたメンバーが定義されている場所

+ +

では、継承されたプロパティとメソッドはどこに定義されているのでしょうか? Objectリファレンスページを見ると、左側に多数のプロパティとメソッドが表示されます。上のスクリーンショットでperson1オブジェクトで使用できた継承されたメンバーの数を超えています。いくつかは継承されており、一部は継承されていません。これはなぜでしょうか?
+
+ 上で述べたように、継承されたものは prototype プロパティ (サブネームスペースと呼ぶこともできます) で定義されたものであり、それは Object.prototype. で始まるものであって、Object. だけで始まるものではありません。prototype プロパティの値はオブジェクトであり、基本的には、プロトタイプチェーンのさらに下のオブジェクトに継承させたいプロパティやメソッドを格納するためのバケットです。
+
+ そのため、Object.prototype.toString()Object.prototype.valueOf() などは、Person() コンストラクタから作成された新しいオブジェクトインスタンスを含め、Object.prototype を継承するあらゆるオブジェクトタイプで利用できます。

+ +

Object.is()Object.keys() など、prototype バケット内で定義されていないメンバは、Object.prototype を継承するオブジェクトインスタンスやオブジェクトタイプには継承されません。これらは、Object() コンストラクタ自身でのみ利用可能なメソッド/プロパティです。

+ +
+

Note: コンストラクタ上で定義されたメソッドが、それ自体が関数であるというのは不思議な感じがします。

+ +

まあ、関数はオブジェクトの型でもあります。信じられないかもしれませんが、Function() のコンストラクタリファレンスを参照してください。

+
+ +
    +
  1. 既存のプロトタイプのプロパティを自分でチェックすることができます。先ほどの例に戻って、JavaScript コンソールに次のように入力してみてください +
    Person.prototype
    +
  2. +
  3. カスタムコンストラクタのプロトタイプに何も定義していないので、出力はあまり表示されません。デフォルトでは、コンストラクタの prototype は常に空から始まります。では、次のようにしてみてください +
    Object.prototype
    +
  4. +
+ +

先ほど示したように、Objectprototype プロパティに定義された多数のメソッドが、Object を継承するオブジェクトで利用できるようになっています。

+ +

プロトタイプチェーン継承の他の例は、JavaScript の至る所で見ることができます。例えば、StringDateNumberArray などのグローバルオブジェクトのプロトタイプに定義されているメソッドやプロパティを探してみてください。これらはすべて、プロトタイプに定義されたいくつかのメンバを持っており、例えばこのように文字列を作るとき

+ +
let myString = 'This is my string.';
+ +

myStringが最初から、split()indexOf()replace()などの便利なメソッドを多数持っている理由です。

+ +
+

Note: このセクションを理解して、もっと知りたいと思ったら、JavaScript でのプロトタイプの使用 についてのより詳細なガイドを読む価値があります。このセクションは、これらの概念に初めて出会ったときに少しでも理解しやすくするために、意図的に簡略化しています。

+
+ +
+

重要: prototype プロパティは JavaScript の中でも最も紛らわしい名前がついている部分の一つです (this__proto__ でアクセスできる内部オブジェクトです、覚えていますか?)。代わりに prototype は、継承したいメンバを定義したオブジェクトを含むプロパティです。

+
+ +

create() の再訪

+ +

先ほど、Object.create() メソッドを使用して新しいオブジェクトのインスタンスを作成する方法を紹介しました。

+ +
    +
  1. 例えば、先ほどの例の JavaScript コンソールでこれを試してみてください +
    let person2 = Object.create(person1);
    +
  2. +
  3. create() が実際に行うことは、指定したプロトタイプオブジェクトから新しいオブジェクトを作成することです。ここでは、person1 をプロトタイプオブジェクトとして使用して person2 を作成しています。これはコンソールで以下のように入力することで確認できます +
    person2.__proto__
    +
  4. +
+ +

これで person1 オブジェクトが返されます。

+ +

コンストラクタのプロパティ

+ +

すべてのコンストラクタ関数は prototype プロパティを持ち、その値は constructor プロパティを含むオブジェクトとなります。この constructor プロパティは、元のコンストラクタ関数を指します。

+ +

次のセクションでお分かりのように、Person.prototype プロパティ (あるいは上のセクションで述べたように、一般的にはコンストラクタ関数の prototype プロパティに定義されているオブジェクト) は、Person() コンストラクタを使用して作成されたすべてのインスタンスオブジェクトで利用可能になります。したがって、コンストラクタプロパティは person1person2 の両方のオブジェクトでも利用可能です。

+ +
    +
  1. 例えば、コンソールで次のコマンドを試してみてください +
    person1.constructor
    +person2.constructor
    + +

    これらのインスタンスの元の定義を含む Person() コンストラクタを返します。

    + +

    巧妙なトリックとしては、constructor プロパティの最後に括弧を付けて (必要なパラメータを含む)、そのコンストラクタから別のオブジェクトのインスタンスを作成することができます。コンストラクタは結局のところ関数なので、括弧を使用して呼び出すことができます。関数をコンストラクタとして使用したい場合は、new キーワードを含めて指定する必要があります。

    +
  2. +
  3. これをコンソールで試してみてください +
    let person3 = new person1.constructor('Karen', 'Stephenson', 26, 'female', ['playing drums', 'mountain climbing']);
    +
  4. +
  5. では、新しいオブジェクトの機能にアクセスしてみましょう +
    person3.name.first
    +person3.age
    +person3.bio()
    +
  6. +
+ +

これはよく機能します。頻繁に使用する必要はありませんが、新しいインスタンスを作成したいときに、何らかの理由で元のコンストラクタへの参照が簡単に利用できない場合に非常に便利です。

+ +

constructor プロパティには他の用途もあります。たとえば、オブジェクトのインスタンスがあり、そのインスタンスのコンストラクタの名前を返したい場合は次のようにします。

+ +
instanceName.constructor.name
+ +

たとえば、これを試してみてください:

+ +
person1.constructor.name
+
+ +
+

Note: constructor.name の値は (プロトタイプの継承、バインディング、プリプロセッサ、トランスパイラなどによる) 変わる可能性があります。そのため、より複雑な例では、代わりに instanceof 演算子を使用することになります。

+
+ +
    +
+ +

プロトタイプの変更

+ +

コンストラクタ関数の prototype プロパティを変更する例を見てみましょう - メソッドは、コンストラクタから作成されたすべてのオブジェクトインスタンスで利用可能になります。この時点で、最後に Person() コンストラクタのプロトタイプに何かを追加します。

+ +
    +
  1. oojs-class-further-exercises.html の例に戻り、ソースコードのローカルコピーを作成します。既存の JavaScript の下に、コンストラクタの prototype プロパティに新しいメソッドを追加する次のコードを追加します + +
    Person.prototype.farewell = function() {
    +  alert(this.name.first + ' has left the building. Bye for now!');
    +};
    +
  2. +
  3. コードを保存してブラウザでページを読み込み、テキスト入力に以下のように入力してみてください +
    person1.farewell();
    +
  4. +
+ +

コンストラクタ内で定義されている人の名前を特徴とする警告メッセージが表示されるはずです。これは本当に便利ですが、さらに便利なのは継承チェーン全体が動的に更新され、コンストラクタから派生したすべてのオブジェクトインスタンスでこの新しいメソッドが自動的に利用できるようになったことです。

+ +

ちょっと考えてみましょう。このコードでは、コンストラクタを定義し、そのコンストラクタからインスタンスオブジェクトを作成し、コンストラクタのプロトタイプに新しいメソッドを追加しています。

+ +
function Person(first, last, age, gender, interests) {
+
+  // プロパティおよびメソッドを定義する
+
+}
+
+let person1 = new Person('Tammi', 'Smith', 32, 'neutral', ['music', 'skiing', 'kickboxing']);
+
+Person.prototype.farewell = function() {
+  alert(this.name.first + ' has left the building. Bye for now!');
+};
+ +

しかし、farewell() メソッドは person1 オブジェクトのインスタンスで利用可能です。そのメンバーは、新たに定義された farewell() メソッドを含むように自動的に更新されます。

+ +
+

Note: 逆に、コンストラクタのプロトタイプに定義されたプロパティを delete 演算子を使用して削除すると、他のすべてのクラスインスタンスからもそれぞれのプロパティが削除されます。

+ +

上記の例では、delete person1.__proto__.farewell または delete Person.prototype.farewell を実行すると、すべての Person インスタンスから farewell() メソッドが削除されます。

+ +

この問題を軽減するために、代わりに Object.defineProperty() を使用することができます。

+
+ +
+

Note: この例がうまく動作しない場合は、oojs-class-prototype.html の例を見てください (ライブでも参照してください) 。

+
+ +

このように定義されたプロパティは柔軟性に欠けるため、prototype プロパティで定義されることはほとんどありません。例えば、次のようなプロパティを追加することができます。

+ +
Person.prototype.fullName = 'Bob Smith';
+ +

これはその person がその名前で呼ばれていないかもしれないので、あまり柔軟性がありません。name.firstname.last から fullName を作成する方がずっと良いでしょう。

+ +
Person.prototype.fullName = this.name.first + ' ' + this.name.last;
+ +

しかし、これではうまくいきません。この場合、this は関数スコープではなくグローバルスコープを参照するからです。このプロパティを呼び出すと undefined を返します。これは、先ほどプロトタイプで定義したメソッドでは問題なく動作したのはそれがオブジェクトのインスタンススコープに正常に転送される関数スコープ内にあるためです。そのため、プロトタイプ上で不変の(つまりだれも変更する必要のない)プロパティを定義することもできますが、一般的にはコンストラクタ内でプロパティを定義する方がうまくいきます。

+ +

実際、多くのオブジェクト定義でよく見られるパターンは、コンストラクタ内でプロパティを定義し、プロトタイプ上でメソッドを定義することです。これにより、コンストラクタにはプロパティの定義のみが含まれ、メソッドは別のブロックに分割されるため、コードが読みやすくなります。例えば、以下のようになります。

+ +
// Constructor with property definitions
+
+function Test(a, b, c, d) {
+  // プロパティ定義
+}
+
+// 最初のメソッド定義
+
+Test.prototype.x = function() { ... };
+
+// 第二のメソッド定義
+
+Test.prototype.y = function() { ... };
+
+// など
+ +

このパターンは、Piotr Zalewa 氏の学校計画のアプリの例で実際に見られます。

+ +

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

+ +

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

+ +

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

+ +

まとめ

+ +

この記事では、プロトタイプオブジェクトチェーンによってオブジェクトが互いに機能を継承する方法、プロトタイププロパティとそれを使ってコンストラクタにメソッドを追加する方法など、JavaScript オブジェクトのプロトタイプを取り上げてきました。

+ +

次の記事では、2つのカスタムオブジェクト間で機能の継承を実装する方法を見ていきます。

+ +

{{PreviousMenuNext("Learn/JavaScript/Objects/Object-oriented_JS", "Learn/JavaScript/Objects/Inheritance", "Learn/JavaScript/Objects")}}

+ +

このモジュール

+ + diff --git a/files/ja/learn/javascript/objects/test_your_skills_colon__object_basics/index.html b/files/ja/learn/javascript/objects/test_your_skills_colon__object_basics/index.html new file mode 100644 index 0000000000..d284729df3 --- /dev/null +++ b/files/ja/learn/javascript/objects/test_your_skills_colon__object_basics/index.html @@ -0,0 +1,101 @@ +--- +title: 'スキルテスト: オブジェクトの基本' +slug: 'Learn/JavaScript/Objects/Test_your_skills:_Object_basics' +translation_of: 'Learn/JavaScript/Objects/Test_your_skills:_Object_basics' +--- +
{{learnsidebar}}
+ +

このスキルテストの目的は、JavaScript オブジェクトの基本の理解度をテストすることです。

+ +
+

注意: 以下のインタラクティブなエディターでソリューションを試すこともできますが、コードをダウンロードし、CodePen, jsFiddleGlitchのようなオンラインツールを使用してタスクを実行すると役立つ場合があります。
+
+ 行き詰まった場合は、助けを求めてください —  このページの下部にある評価またはさらなる支援セクションを参照してください。

+
+ +
+

注意: 以下の例では、コードにエラーがある場合、ページの結果パネルに出力され、答えを見つけ出すのに役立ちます(ダウンロード可能なバージョンの場合は、ブラウザーのJavaScriptコンソールに)

+
+ +

オブジェクトの基本 1

+ +

このタスクでは、オブジェクトリテラルが与えられます。あなたのタスクは下記です。

+ + + +

以下のライブコードを更新して、完成した例を再現してみてください。

+ +

{{EmbedGHLiveSample("learning-area/javascript/oojs/tasks/object-basics/object-basics1.html", '100%', 400)}}

+ +
+

このタスクの開始点をダウンロードして、お好きなエディターまたはオンライン・エディターで作業してください。

+
+ +

オブジェクトの基本 2

+ +

次のタスクでは、お気に入りのバンドの1つを表す、自分だけのオブジェクトリテラルを作成してみましょう。必要な要素は次のとおりです。

+ + + +

albums 配列には、少なくとも2つのアルバムを含めること。

+ +

​これが終わったら、変数bandInfoに、名前、国籍、活動年数、スタイル、最初のアルバムのタイトルと発売日などの少しの詳細を書き込みます。

+ +

以下のライブコードを更新して、完成した例を再現してみてください。

+ +

{{EmbedGHLiveSample("learning-area/javascript/oojs/tasks/object-basics/object-basics2.html", '100%', 400)}}

+ +
+

このタスクの開始点をダウンロードして、お好きなエディターまたはオンライン・エディターで作業してください。

+
+ +

オブジェクトの基本 3

+ +

最後に、「オブジェクトの基本」のまとめとして、タスク#1のcatオブジェクトリテラルに戻りましょう。 「Hello, said Bertie the Cymric.」と記録されるように、greeting()メソッドを書き直してください。 ブラウザのDevToolsのコンソールにアクセスしますが、名前や品種に関係なく、同じ構造のすべてのcatオブジェクトで機能します。

+ +

完了したら、cat2という独自のオブジェクトを作成します。このオブジェクトは、同じ構造、まったく同じgreeting()メソッドを持ちますが、名前、品種、色が異なります。

+ +

両方のgreeting()メソッドを呼び出して、適切なあいさつ(greeting)がコンソールに記録されることを確認します。

+ +

コードはあまりDRYではありません(それぞれは1回だけ定義すること)—たとえば、同じメソッドを2回定義しています。 どうすればもっとDRYにすることができますか? よくわからない場合でも、心配しないでください。これは、シリーズの今後の記事で取り上げる予定です。

+ +

以下のライブコードを更新して、完成した例を再現してみてください。

+ +

{{EmbedGHLiveSample("learning-area/javascript/oojs/tasks/object-basics/object-basics3.html", '100%', 400)}}

+ +
+

このタスクの開始点をダウンロードして、お好きなエディターまたはオンライン・エディターで作業してください。

+
+ +

まとめとヘルプ

+ +

自分のコードの評価が欲しい、または行き詰まって助けを求めたい場合:

+ +
    +
  1. CodePen、jsFiddle、Glitchなどのオンライン共有可能なエディターで作業をします。コードを自分で作成することも、上記のセクションでリンクされているスターティングポイントファイルを使用することもできます。
  2. +
  3. MDNDiscourseフォーラムの学習カテゴリで評価や支援を求める投稿を書いてください。投稿には次のものを含める必要があります。
  4. +
+ + -- cgit v1.2.3-54-g00ecf