いまをときめくJavaScriptに関する知識と技術の向上を目指しながら、
インフォニック発JavaScriptライブラリを世界に発信することを目論むプロジェクトです。

他にもWebアプリケーションにおけるUI構築技術全般をターゲットにしています。

公開日:2008.01.07

クラス - 基本

今回から本格的にオブジェクト指向のJavaScriptを説明していきます。 JavaScriptはプロトタイプベースオブジェクト指向言語です。 C++やJavaでお馴染みのクラスベースオブジェクト指向とは、 少しイメージが異なります。 、、、が実際使う分にはそれほど変わらないので構える必要はありません。

前回までのおさらい

オブジェクトは実行時に作成されると説明しました。 では『ニワトリ』というオブジェクトを作成してみましょう。

ニワトリはプロパティに

  • 名前(name)

メソッドに

  • 産む(lay)
  • 鳴く(crow)

を持つものとしてコードに表します。

var chicken = {};
chicken.name = "しゃくれ";
chicken.lay = function(){ alert(this.name + "は卵を産んだ"); };
chicken.crow = function(){ alert("こけーっ"); };

ではもう一匹作ってみましょう。

var chicken2 = {};
chicken.name = "きむさん";
chicken.lay = function(){ alert(this.name + "は卵を産んだ"); };
chicke.crow = function(){ alert("こけーっ"); };

では三匹目を・・・

同じような振る舞いを持つオブジェクトに毎回、 同じ処理をセットするのでしょうか? もちろんそんな事はありません。

クラスの定義

上のニワトリの場合、名前は変わりますが、 メソッド(振る舞い)は変わりませんね。

こんな場合はクラスとしてオブジェクトの雛形を作ってやります。 実際にコードを見てみましょう。

function Chicken(name) {
  this.name = name;
  this.lay = function(){ alert(this.name + "は卵を産んだ"); };
  this.crow = function(){ alert("こけーっ"); };
}

そしてこれを使い、それぞれのニワトリを作ってみます。

var syakure = new Chicken('しゃくれ');
var kimusan = new Chicken('きむさん');

syakure.lay(); //⇒ 「しゃくれは卵を産んだ」と表示
syakure.crow(); //⇒ 「こけーっ」と表示
kimusan.lay(); //⇒ 「きむさんは卵を産んだ」と表示
kimusan.crow(); //⇒ 「こけーっ」と表示

クラスベース風に説明すると、 function Chicken ~で「Chickenクラス」を作成しています。 そして new でChickenクラスの「インスタンス」を作成する事ができ、 「コンストラクタ」としてChicken関数が実行されます。 要はクラスとは関数オブジェクトの事なんです。

またChicken関数内で呼び出されているthisキーワードは、 インスタンス変数(メンバー)へのアクセスの為のキーワードと ここでは覚えておいてください。Javaでもthisですね。※1

※1 言語仕様上の本来の意味はもっと複雑ですが、別の機会に説明します。

インスタンス

ではkimusanの方にだけ、拡張を加えてみましょう。

kimusan.kick = function(){ alert('やくざキーック'); };
kimusan.kick();
    //⇒ 「やくざキーック」と表示

メソッドが新たに追加されました。

ではもう一匹の方はどうなってるでしょう?

syakure.kick();
    // エラーとなり実行できない。

インスタンス毎に振る舞いを変えられて非常に柔軟です。 では次にコンストラクタで指定されたプロパティを書き変えて 鳴けないニワトリにしてみましょうか。

kimusan.crow = function(){ alert("・・・"); };
kimusan.crow();
    // ⇒「・・・」と表示

ではもう一匹の方はどうなったでしょうか?

syakure.crow();
    // ⇒「こけーっ」と表示

インスタンスに対する操作はあくまでもそのインスタンスに対して だけだということがわかります。 言い換えればインスタンス毎に関数を持っている事になります。 処理が変わる事のなさそうな関数まで インスタンス毎ってなんか非効率そうですよね。

じゃあ関数だけ外で別の変数にいれておいて、 インスタンス毎にその参照だけ渡せばいいんじゃない? こんな風に。

var func_crow = function(){ alert("こけーっ"); };

function Chicken(name) {
  this.name = name;
  this.crow = func_crow;
}

var kimusan = new Chicken("きむさん");
kimusan.crow();
    //⇒「こけーっ」と表示

いけそうですね。 実はJavaScriptでは予めこの仕組みを提供しています。 この仕組みを使えばもっと複雑な事もできます。 次回はこの仕組みについて説明したいと思います。

thisの挙動

そうそう、一つ忘れていました。 上記のコードでコンストラクタの外で宣言した関数内でthisを参照したら どうなるでしょう?ではやってみましょう。

var func_lay = function(){ alert(this.name + "は卵を産んだ"); };

function Chicken(name){
  this.name = name;
  this.lay = func_lay;
}

var kimusan = new Chicken("きむさん");
kimusan.lay();
     //⇒ 「きむさんは卵を産んだ」と表示

望み通りの動きをしますね。 では、func_layを直接呼んだらどうなるでしょう?

func_lay();
    //⇒ 「は卵を産んだ」と表示

name部分が何も表示されません。 これはfunc_lay()として呼んだ場合、 this = window として参照されている為です。 実際に見てみましょう。

window.name = "ういんどう";
func_lay();
    //⇒ 「ういんどうは卵を産んだ」と表示

オブジェクトを省略してプロパティにアクセスした場合は、 windowが省略されているのと同じなので、 func_lay()と呼び出すのはwindow.func_lay()として 呼び出すのと同じなんですね。 window.func_lay()の時は、this = window kimusan.lay()の時は、this = kimusan としてthisは実行時のオブジェクトを指す事になっています。 少しややこしいですが、すぐに慣れると思います。

まとめ

  • 関数はオブジェクトを作成する為のコンストラクタである。
  • 関数内でのthisは関数を呼び出したオブジェクトを指している。
  • クラスは関数オブジェクト。