Summary. ある意味JavaScriptの根幹をなすともいえる「オブジェクト」についてです。

オブジェクトって何?

誤解を恐れずに言えば、 オブジェクト とは「データの集まり」のことです[1]。初めからJavaScriptで用意されている「配列(Array)」などはまさにその代表例です。

しかし、オブジェクトは単なるデータの塊というわけではありません。通常彼らには「メソッド」という関数みたいなものが定義されています。やや比喩的な言い回しですが、用意されたメソッドを呼び出せば、彼らに色々な仕事をやらせることができます。その意味では「便利な小間使い」みたいなものともいえます。

例を見てみましょう。たとえば
a = new Array( 1, 2, 3 );
a.reverse(); 
と書いた場合、まず「配列」という構造のオブジェクトが作られます。その後、このオブジェクトのもつ reverse というメソッドが呼び出され、中身が逆順に入れ替わります。利用者が中身をごちゃごちゃいじる必要はなくて、単にメソッドを呼べばいいわけです。

例: 日付オブジェクト

JavaScriptにはたくさんの種類のオブジェクトが用意されています。その中でいかにも「小間使い」という感じがするのはこの Date クラスじゃないかと思います。
var date = new Date();
window.alert(date.toLocaleString());
window.alert(date.toUTCString());
1行目で現在時刻を表すオブジェクトが作られます。"January 10, 2010 12:34:5" のような引数を渡せば現在時刻以外の時刻を表すオブジェクトを作ることもできます。

Dateオブジェクトには、それが表す時間を色々なフォーマットで表示するメソッドが用意されています。中でも、日本からアクセスされたら日本時間を、イギリスからならイギリス時間を・・・、という date.toLocaleString() はお世話になる機会が多いのではないかと思います。

試すと分かりますが、単に日本時間に直すだけでなく表示も日本風になります。一方、date.toUTCString() は世界標準時を返すメソッドで、こちらは英語になります。これらの雑務は人間が毎回毎回コーディングするような代物ではありません。オブジェクトに任せましょう、というわけです。

例: ウィンドウオブジェクト

普段何気なく使っているオブジェクトの代表例は window でしょう。さっきも window.alert() が出てきましたね。これもオブジェクトで、現在Webページを開いているウィンドウを管理しています。非常に多岐にわたるメソッドを持っていて、まともに紹介しようとしたらちょっと大変です。

世間で出回っているJavaScriptのTips集では、この window に命令を出して何かやらせるケースが多いです。空気のような存在ですが大事なオブジェクトといえます。

オブジェクトを作る

実はこのオブジェクトというもの、自分で1から作ることもできます。

完全に手作業で作る

obj = new Object();
 
obj.name = "Adam";
obj.age = 18;
 
obj.hello = function(target){
  window.alert("Hello, " + target + ". I'm " + this.name + ".");
} 
まず1行目で obj という真新しい空っぽのオブジェクトが作られます。このオブジェクトに名前を付けたり、年齢を決めたりしてあげます。最後に hello() という、挨拶を行わせるメソッドを定義しています。

メソッドの定義の中に出てくる this とは、この仕事を実行するオブジェクト自身のことを指します。自分の名前を名乗ってもらうためには this.name と書きます。

クラスを経由して作る

さっきの例だとオブジェクトを1個ずつ自分で作らないといけないので、あまりありがたみがありません。そこで普通は「テンプレ」[2]を用意して量産します。
Person = function(na){
  this.name = na;
}
 
Person.prototype.talk = function(a){
  window.alert(this.name + ": Hi, " + a.name + ". How are you?");
  a.reply(this);
}
 
Person.prototype.reply = function(a){
  window.alert(this.name + ": I'm fine, thank you.");
}
 
boy = new Person("Tom");
girl = new Person("Alice");
 
boy.talk(girl);
girl.talk(boy); 
まず最初に Person という関数を作ります。JavaScriptは不思議なプログラミング言語で、なぜか関数を作るとそれを new できるようになります。実行すると、name というフィールドを持ったオブジェクトが返されます。

メソッドを持たせるには、上のように Person.prototype.なんちゃら という関数を定義します。そうすると、new したとき自動的にメソッドが追加されます。回りくどいですが、効率上の都合でこうなってるらしいです。

メリット

構造体的なこと

たとえば今、トランプを使ったゲームを制作しているとしましょう。別に本当に作るわけじゃないので細かい仕様は考えませんが、どんなゲームであれ「山札から1枚カードを引く」という関数は必要になるでしょう:
drawCard = function(){
  ......
  return カード;
} 
さて、たとえば「スペードの3」を結果として返すときにどう書いたらいいでしょう? これが "スペード" だけなら文字列で返せばいいですし、数字だけなら int で返せばいいです。しかし、実際はこれらを組にして返さないと意味がありません。

まあ、逃げ道は色々あるんですが、オブジェクトを利用すれば
Card = function(suit, num){
  this.suit = suit;
  this.number = num;
} 
みたいなクラスを定義しておいて、このクラスのオブジェクトを返す方法が考えられます。これならトランプ以外の、もっと複雑な情報を持つカードでも応用できます。

名前空間的なこと

上のコードに出てきた this.suit や this.number などのことを一般的に「フィールド」または「メンバ変数」と呼びます。当たり前ですが、これらはちゃんと . を使って指定しないとアクセスできません。したがって、
var suit = "スペード";
var card = new Card(suit, 3);
 
......
 
suit = "ハート"; // 広域変数の suit だけ再代入される
のようなコードを書いても card には何の変化もありません。逆に言うと、広域変数のほうの suit はいつどこで内容が書き換わるかわからないということです。意図して書き換えるならともかく、うっかり勘違いをして上書きしてしまった場合はバグの原因になります。

特に恐ろしいのは、他の人の書いたコード(自分が書いたコードでも半年以上前だと他人みたいなもんです)と自分の書いたコードを混ぜ合わせるときです。変数や関数の名前がたまたまダブっていた場合、どこかしらで予期せぬ不具合が発生します。事前に対処すればいいのですが、こういうのはすごくタルいです。

それに比べると、メンバ変数のほうは頑丈です。

他の人のコードの中で上の card.suit をついうっかり書き換えることなんて考えられるでしょうか? いやまあ、たまたまその人も card という変数を使っていた・・・って可能性はありますが、それは card が原因なわけで。わざわざ名前の重複回避に配慮しなくても、余程のことが無い限りうまくいきます。

再利用

オブジェクトは関連するデータをひとまとめにしたものです。そのため、フィールドの数が10だろうが20だろうがバラバラになりにくいです。加えて、大抵の仕事はメソッドを通じてオブジェクト自身にやらせるので、利用者は細かい裏事情について知る必要がありません。

これらの理由から、一度作ったクラスは比較的気楽に再利用できることが多いです(もちろん設計次第だが)。このあと字句解析で Lexer というクラスを作りますが、使い方さえ覚えておけば細かい実装など忘れてしまっていいわけです。





[1]
細かいことを言い出すと色々難しいのだけど。別にフィールドがなくてもオブジェクトなわけだし。

[2]
普通は「クラス」と呼ばれる。プログラミング言語によっては、クラスを使って作られたオブジェクトをそのクラスの「インスタンス」と呼ぶこともある。
最終更新:2012年12月10日 10:52