【3/18〜】Amazon、VMwareが語る『クラウドの未来』 スラッシュドット    はてなブックマーク  Yahoo!ブックマークに登録  印刷
 

連載:Ajax時代のJavaScriptプログラミング再入門

第4回 JavaScriptでオブジェクト指向プログラミング

山田 祥寛(http://www.wings.msn.to/
2007/09/25
Page1 Page2 Page3 Page4


Back Issue
1
JavaScriptの復権
2 JavaScriptの関数をマスターしよう
3 変数の宣言とスコープ

 JavaScriptが、いま注目を浴びている。

 JavaScriptがこれだけの注目を浴びた理由の1つとして、Ajax技術の登場とも相まって、JavaScriptに対する確かな理解の必要性が高まったという事情は否定できない。しかし、それだけでは説明できない急速な注目の理由として、もう1つ、JavaScriptという言語そのものが持つユニークさが開発者の目を引いたという点は看過できないだろう。

 もっとも、このユニークさは同時に、多くの開発者が感じているJavaScriptに対する苦手意識と同義でもある。これまでVisual BasicやC#、Javaといった言語でオブジェクト指向構文になじんできた開発者にとって、JavaScriptのオブジェクト指向構文はいかにも奇異なものに映るのだ。ようやくクラスという概念を理解した開発者が、JavaScriptという言語の背後にたびたび見え隠れする「プロトタイプ・ベースのオブジェクト指向」というキーワードに、そもそもの敬遠感を抱いているという事情もあるだろう。

 しかし、JavaScriptにおける「プロトタイプ・ベースのオブジェクト指向」は何ら目新しい概念ではない。ごくごく単純化していってしまえば、プロトタイプとは「より縛りの弱いクラス」のようなものと思っていただければよいだろう。

 連載最終回となる今回は、JavaScriptで本格的なプログラミングを行うのに欠かせない――そして、JavaScriptに対する苦手意識の根幹でもある(と思われる)「プロトタイプ・ベースのオブジェクト指向」構文について解説する。

 なお、本論に入るに当たって、1点のみ注意していただきたい点がある。第1回でも述べたように、JavaScriptでは厳密な意味でのクラスという概念は存在しない。しかし、本稿を読まれている読者の多くはクラス・ベースのオブジェクト指向構文になじんでいると思われることから、ここではJavaScriptにおいて「クラス的な役割を持つ存在」を、便宜上、「クラス」と呼ぶものとする。ご了承いただきたい。

JavaScriptにおける“クラス”の定義

 JavaScriptにおけるオブジェクト指向構文について理解するには、抽象的な解説を重ねるよりも、まずは具体的なクラスを実際に作成してみた方が話が早いだろう。以下は、JavaScriptで中身を持たない、最も単純なクラスを定義した例だ。

var Animal = function() {};
リスト1 JavaScriptにおける最もシンプルなクラスの例

 変数Animalに対して、空の関数リテラルを代入しているだけのコードだ。「これがクラス?」と思われた方もいるかもしれないが、そのとおり、これがJavaScriptにおけるクラスなのである。れっきとしたクラスである証拠に、

var anim = new Animal();

と、クラス・ベースのオブジェクト指向構文でもおなじみのnew演算子を利用して、実際にインスタンス化を行ってみると、確かに正しく(エラーなども出ずに)実行できることが確認できる。ここではまず、

JavaScriptでは関数オブジェクトにクラスとしての役割を与えている

という点を覚えておこう。

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

 コンストラクタとは、オブジェクトを生成する際に自動的に呼び出される関数(メソッド)のこと。クラス・ベースのオブジェクト指向構文を少しでもかじったことのある方ならば、これはごく耳になじんだキーワードの1つであるはずだ。

 リスト1で記述したAnimal関数は、new演算子によって呼び出され、オブジェクトを生成するという意味で、厳密には「クラス」そのものというよりも「コンストラクタ」と呼ぶのがより正しいだろう。

 コードを見ても分かるように、JavaScriptにおいては、コンストラクタと(普通の)関数との間に本質的な違いはない。関数として呼び出すのか、それともnew演算子によって呼び出すかによって、関数の振る舞いが変わるわけである。もっとも、(構文規則ではないが)実際のコードで、通常の関数とコンストラクタとが区別できないのは不便であるので、一般的にはコンストラクタ(クラス)名は大文字で始めるのが好ましい。

 さて、先ほどのリスト1では空のコンストラクタを定義したわけであるが、通常、コンストラクタの中では生成するオブジェクトを初期化するためのコードを記述するのが一般的だ。具体的には、オブジェクト共通で利用するプロパティ(メンバ変数)などを定義するのがコンストラクタの役割なのである。

 以下は、リスト1で作成したAnimalクラスにname/sexプロパティを追加した例だ。

var Animal = function(name, sex) {
  this.name = name;
  this.sex = sex;
}
var anim = new Animal("トクジロウ", "オス");
window.alert(anim.name + ":" + anim.sex); // 「トクジロウ:オス」
リスト2 Animalクラスにname/sexプロパティを追加したコード

 ここで注目していただきたいのは、コンストラクタの中のthisキーワードだ。コンストラクタとして関数オブジェクトを呼び出した場合、thisキーワードは新たに生成されるオブジェクトを表すことになる。そして、

this.プロパティ名 = 値

のように記述することで、オブジェクトにプロパティを追加することができるというわけだ。実際、リスト2の例でも、コンストラクタで設定されたname/sexプロパティが、作成したオブジェクトから正しく参照できることが確認できるはずだ。

[注意]コンストラクタに戻り値は不要

 もう1点、コンストラクタを定義する場合のささやかな注意点がある。というのも、コンストラクタでは「戻り値を返す必要がない」という点だ。クラス・ベースのオブジェクト指向構文に慣れていればごく当たり前のポイントではあるが、プロトタイプ・ベースのオブジェクト指向構文でもこの点は同様であるので、あらためて注意しておきたい。コンストラクタ関数の役割は、あくまでこれから生成するオブジェクトの初期化を行うことであって、オブジェクトそのものを返すことではない。

■メソッド − コンストラクタによる定義 −

 いまさらいうまでもなく、オブジェクトの構成は大きく「データ」と「手続き」とに分類できる。先ほどは、プロパティ(メンバ変数)を定義することでオブジェクトの「データ」を定義したので、当然の流れとして、次は「手続き」の部分――「メソッド(メンバ関数)」を定義してみることにしよう。

 以下は、先ほどのAnimalクラスにtoStringという名前でメソッドを追加した例だ。

var Animal = function(name, sex) {
  this.name = name;
  this.sex = sex;
  this.toString = function() {
    window.alert(this.name + " " + this.sex);
  };
}
var anim = new Animal("トクジロウ", "オス");
anim.toString(); // 「トクジロウ オス」
リスト3 AnimalクラスにtoStringメソッドを追加した例

 JavaScriptには厳密な意味でのメソッドという概念はない。このリスト3を見ても分かるように、「値として関数オブジェクトが渡されたプロパティがメソッドと見なされる」わけだ。ここではtoStringという名前のプロパティに匿名関数を引き渡すことで、toStringというメソッドを追加したことになる。

 果たして、toStringメソッドを実行してみると、確かに「トクジロウ オス」というメッセージが返されることが確認できるはずだ。

■メソッド − インスタンスへの追加 −

 もっとも、JavaScriptではメソッドをあらかじめコンストラクタで定義できるばかりではない。インスタンス化されたオブジェクトに対しても、後からメンバを追加できるのがJavaScriptの特徴だ(これをJavaScriptの「動的性質」と呼ぶ)。

 そのJavaScriptの動的性質を利用することで、先ほどのリスト3は以下のように書き換えることも可能だ。

var Animal = function(name, sex) {
  this.name = name;
  this.sex = sex;
}
var anim = new Animal("トクジロウ", "オス");
anim.toString = function() {
  window.alert(this.name + " " + this.sex);
};
anim.toString(); // 「トクジロウ オス」
リスト4 インスタンスに対してメソッドを追加した例

 先ほど同様、toStringメソッドが正しく動作していることが確認できるはずだ。ただし、インスタンスに対して動的にメソッドを追加した場合には、注意も必要だ。

 以下に、Animalクラスに対するインスタンスを2つ登場させた例を見てみよう。

var Animal = function(name, sex) {
  this.name = name;
  this.sex = sex;
}
var anim = new Animal("トクジロウ", "オス");
anim.toString = function() {
  window.alert(this.name + " " + this.sex);
};
anim.toString(); // 「トクジロウ オス」

var anim2 = new Animal("リンリン", "メス");
anim2.toString(); // エラー発生
リスト5 Animalクラスに対してインスタンスを複数生成した例

 インスタンスanim2からtoStringメソッドを呼び出そうとした時点で、エラーが発生することが確認できる。Internet Explorerでは、恐らく「オブジェクトでサポートされていないプロパティまたはメソッドです。」というエラーメッセージが出力されるはずだ。

 このことから、インスタンスへのメソッドの追加はあくまでインスタンス(ここではanim)への追加であって、「大本であるクラスへの追加ではない」ことが理解できる。つまり、クラス・ベースのオブジェクト指向言語においては、あるクラスを基に作成されたインスタンスは必ず同じメンバを持つはずであるが、JavaScriptでは同一の“クラス”を基に作成されたインスタンスでも異なるメンバを持つ可能性がある、ということなのである。これが、先ほどプロトタイプ・ベースのオブジェクト指向が、クラス・ベースのそれよりも「縛りが弱い」と述べた理由でもある*1

*1 ちなみに、ここではメンバを追加する例について述べたが、インスタンス、あるいはコンストラクタ経由で追加したメンバはdelete演算子(後述)で削除することも可能だ。

 いずれにせよ、ここでは、

インスタンスに追加したメンバは、
(オブジェクト共通ではなく)そのインスタンスのみで有効である

という点を押さえておこう。


 INDEX
  Ajax時代のJavaScriptプログラミング再入門
  第4回 JavaScriptでオブジェクト指向プログラミング
  1.JavaScriptにおける“クラス”の定義
    2.プロトタイプ・ベースのオブジェクト指向
    3.プロトタイプ・チェーン − JavaScriptの継承機構 −
    4.[コラム]オブジェクトと連想配列
 
インデックス・ページヘ  「Ajax時代のJavaScriptプログラミング再入門」

ホワイトペーパーTechTargetジャパン

Insider.NET フォーラム 新着記事

@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

RSSフィード

スキルアップ/キャリアアップ(JOB@IT)

- PR -
- PR -

お勧め求人情報

キャリアアップ 〜JOB@IT
@IT Special -PR-
  TomcatやJBossなどAPサーバ環境に関する
情報を集約! “業務”用APサーバ大百科

New!
  一気に解説! 最新のクラスタストレージ
「RAIDを超えたストレージ基準」……など

New!
  クラウド的ユーザー体験の変化は脅威か?
仮想化技術を使いこなす運用管理術を紹介

New!

  上司や部下、部署内メンバーとの情報共有
を“ガラッ”と変えるコラボツールとは?

New!
  おばかアプリ選手権、第4弾開催中!!
ムダにカッコよくてくだらない作品求ム!

  社内ファイルサーバを“クラウド”に統合
VPN直結「クラウド型ストレージ」を紹介

  Twitterのアカウントはなぜ突破された?
メールによる新手の攻撃手法とその対策

  もう仮想化のお試しフェイズは終わりだ!
Hyper-V 2.0が基幹システムも仮想化

  美人!? まあまあ? 気になる いやし系!!
PV急増で「美人時計」がとった手段とは?

  クライアント企業から求められる人材
⇒IT技術と経営戦略を併せ持つ「戦略家」

  .NET編集長が実践する「技術情報検索術」
サンプル・コードを簡単に探す“技”は?

  業務効率と情報セキュリティ対策を両立!
手間なく確実に機密情報を守る方法とは?

  進化を続ける富士通ストレージETERNUS DX
製品開発者の自信を裏付けるものとは何か

  運用管理の課題を“2つの観点”から分析
ユーザー満足度の高い「仮想環境」とは?

  【CTC事例】約30の基幹システムを統合!
膨大なバッジジョブを制御した方法は?

  仮想化すればコストは削減できるか?
仮想化に必要な「3つの視点」を解説する

  その数、なんと400台以上! グループ内
サーバの「統合管理」によるメリットは?