【コラム】

そろそろきっちりJavaScript

3 プロトタイプオブジェクトについて考える

富田陽介  [2007/03/12]

オブジェクトにメソッドを定義する

前回の記事では、JavaScriptでオブジェクトを定義する記法を紹介した。オブジェクト指向プログラミングの利点のひとつに、プログラムの再利用性の向上が挙げられるだろう。既にあるオブジェクトをそのまま流用したり、用途に合わせて既存のオブジェクトを拡張・再構成したりすることにより、必要な概念や機能をゼロから実装する時間やリスクを低減することができる。JavaScriptでのオブジェクト指向の利点も、この例に洩れない(オブジェクト指向プログラミングそのものに関する解説は、本稿の主旨では無いので、別途書籍等を参考いただきたい)。

また、JavaScriptには、「プロトタイプオブジェクト」という特徴的な概念が存在する(このため、JavaScriptは「プロトタイプベース言語」と表現されることもある)。プロトタイプオブジェクトにより、JavaやC++のようなプログラミング言語とは異なるアプローチで、既存のオブジェクトを流用していく過程を実感できるだろう。プロトタイプオブジェクトは、JavaScriptでのオブジェクト指向プログラミングでは欠かせない概念であるため、今後詳細に紹介していく。

まずは、前回に引き続きJavaScriptでのオブジェクト定義方法を見ていこう。前回の記事で述べた通り、JavaScriptでは、オブジェクトは「プロパティ名とそれに対応づけられた値のペアのリスト」である。オブジェクトが持つメソッドも、前回紹介した、プロパティ名と値のペアをコロン":"で区切る記法により定義することができる。Firebugで次のコマンドを実行し、動作を確認してみよう。連載第1回で紹介した関数リテラルの定義方法(無名関数を特定のプロパティ(変数)名に割り当てる)も思い出していただきたい。

例1

/* 'run' プロパティに無名関数を割り当てることで、メソッドとして定義する */
>>> var Car = { 'mfr':'Toyota', 'model':'FunCargo', 'run':function(){ alert('ぶーん') } }
/* 定義したメソッドをコールする */
>>> Car.run()

また、オブジェクトの中では、オブジェクトを入れ子に定義することもできる。

例2

/* 例1に続いて実行 */
/* GarageのmyCarプロパティに既存のCarオブジェクトを割り当てる */
>>> var Garage = { 'number':'7', 'myCar':Car }
/* 定義したメソッドをコールする */
>>> Garage.myCar.run()

プロパティを削除する

オブジェクトでのプロパティ定義方法に加えて、プロパティを削除する方法も確認しておこう。プロパティを削除する方法は delete 演算子を使う方法と、undefined キーワードをセットする方法の2種類がある。両者とも削除という点では似たような振る舞いをするが、削除後のプロパティの扱いに違いがある。次のコマンドを実行して動作を確認しておこう。

例3

/* 配列を定義する */
>>> var number = [0,1,2,3,4]
/* delete演算が成功するとtrueが返る */
>>> delete number[1]
true
/* undefinedキーワードは、代入するだけ */
>>> number[3] = undefined
/* toString()ではオブジェクトの文字列表現が取得できる */
/* 指定したプロパティが削除されていることを確認 */
>>> number.toString()
"0,,2,,4"
/* deleteではプロパティそのものが削除されているが、 */
/* undefinedではプロパティ自体は存在したまま(値のみ未定義)となる */
>>> 1 in number
false
>>> 3 in number
true

JavaScriptでは全てがオブジェクト?

これまで、JavaScriptでのオブジェクト定義方法を紹介してきた。実は、JavaScriptで登場するデータは(数値などのプリミティブなデータを除き)、すべてオブジェクトである。また、すべてのオブジェクトは、最上位の親オブジェクトである Object の子オブジェクトとして定義されている。

例3で記述した配列は Array オブジェクトの子オブジェクトとして定義されたものである。オブジェクトの構成(「プロパティ名とそれに対応づけられた値のペアのリスト」であること)を思い出していただければ、delete演算子やundefinedキーワードが何をおこなっているかについて、より理解できるだろう。

function() で定義できるすべての関数もまた Function オブジェクトの子オブジェクトとして定義される、れっきとしたオブジェクトである。例1で述べたオブジェクトに対するメソッド定義は、例2で見たようなオブジェクトを入れ子定義する方法を用いて、Carオブジェクトの内部プロパティとして関数オブジェクトrunを持たせた、に過ぎない。

ところで、他のオブジェクト指向言語をご存知の読者であれば、ここまでの説明に「クラス」という表現が登場していない事にお気付きだろうか。これは、筆者が意識的に使用を控えてきていた表現である。

JavaやC++のようなオブジェクト指向言語においては「クラス」を定義した上で、これを"new"し、インスタンスを生成するのが一般的だろう。しかしながら、誤解を恐れずに言えば、JavaScriptでは(少なくとも現時点では)「クラス」という概念は存在しない。便宜的に"クラスのように利用できるオブジェクト"を定義することはできるが、これは本来の意味でのクラスとは異なるものである(書籍によっては「JavaScriptのクラス」を解説しているものもあるが、便宜的またはインフォーマルな表現として登場しているはずだ)。

「クラスが存在しない」といった事は上記だけでは理解し難い点がある事とおもう。これらを理解するためには、冒頭で述べた「プロトタイプオブジェクト」について理解し、JavaScriptでのオブジェクト指向プログラミングの特徴をしっかりと押さえておく必要がある。これらについて、次回以降、解説していこう。

    新着記事

    特設サイトの情報

    求人情報

    人気記事

    一覧

    イチオシ記事

    新着記事

    特別企画

    一覧

    転職ノウハウ

    あなたの仕事適性診断
    あなたの仕事適性診断

    4つの診断で、自分の適性を見つめなおそう!

    Heroes File ~挑戦者たち~
    Heroes File ~挑戦者たち~

    働くこと・挑戦し続けることへの思いを綴ったインタビュー

    はじめての転職診断
    はじめての転職診断

    あなたにピッタリのアドバイスを読むことができます。

    転職Q&A
    転職Q&A

    転職に必要な情報が収集できます

    スカウト転職する
    スカウト転職する

    企業からアプローチのメッセージが届きます。