JavaScriptの関数には、やたらに何でも突っ込める

jQueryのドル記号「$」は、理不尽なくらいに大活躍します。あまりにも何でもできるので、「ありゃ何なんだ?」と思いますよね。$(...) という形で呼び出せるので、関数なのは確かです。JavaScriptでは、ひとつの関数名に対して呆れるくらいにオーバーロード(多義的定義)ができます。

内容:

  1. コンストラクタあるいはクラスとしての関数
  2. メソッドとしての関数
  3. 名前空間あるいはモジュールとしての関数
  4. 関数/オブジェクトとしての関数
  5. 試してみる

コンストラクタあるいはクラスとしての関数

関数は、オブジェクトのコンストラクタとして使用できます。

function Point(x_, y_) {
  this.x = x_ || 0;
  this.y = y_ || 0;
}

お尻にアンダースコアが付いた引数名 x_, y_ は省略可能のつもりです。new Point()、new Point(2)、new Point(2, 3) とかすると、2次元の点を表すオブジェクトが生成されます。

JavaScriptでは、正式なクラスはありませんが、コンストラクタ関数名をクラス名のように使えます。例えば、pt instanceof Point とすれば、オブジェクトptがクラスPointのインスタンスかどうかを判定できます。

メソッドとしての関数

クラスのメソッドは、コンストラクタ関数のprototypeに対して関数を定義すればいいですね。

Point.prototype.moveTo = function(x, y) {
  this.x = x;
  this.y = y;
};

Point.prototype.toString = function() {
  return "(" + this.x + ", " + this.y + ")";
};

これで、Pointのインスタンスであるptに対して、pt.moveTo(3, 4) とか pt.toString() というメソッド呼び出しが可能になります。

Point.prototype.moveTo は関数なので、pt.moveTo(3, 4) は Point.prototype.moveTo.apply(pt, [3, 4]) としても同じです。applyの第1引数ptは、関数Point.prototype.moveToにthisとして渡されます。

名前空間あるいはモジュールとしての関数

関数はオブジェクトでもあるので、そのプロパティとして値や関数を設定することができます。これにより、値や関数をグループにまとめることができます。グルーピングに使われるオブジェクトは名前空間と呼ばれます。名前空間はモジュール化に利用するので、モジュールと呼んでもいいと思います。

Point.norm = function(pt) {
  return Math.sqrt(pt.x*pt.x + pt.y*pt.y);
};

Point.normは、引数で与えられた点の「ノルム=原点(0, 0)からの距離」を求める関数です。Pointクラスのスタティックメソッドと思ってもいいですし、Pointモジュールに属する関数と思ってもかまいません。

関数/オブジェクトとしての関数

関数はもちろん関数です。つまり、Point() のように、newを付けないで呼び出すことができます。コンストラクタ関数にnewを付けずに呼び出すと、通常はundefinedが戻り値として返ります。しかし、別な値を返すようにすることもできます。

JavaScriptでnewが不要なコンストラクタ」において、 if (!(this instanceof クラス名)) という条件により、new付きで呼び出されたか/そうでないかを判定する方法を紹介しました。この方法を使うと、次のようなことができます。

function Point(x_, y_) {
  if (!(this instanceof Point)) {
    return Point.help;
  }
  this.x = x_ || 0;
  this.y = y_ || 0;
}

Point.help = 
  "\n"
  + "new Point(x, y) -- 新しい点を生成、x, y は省略可能\n"
  + "Point() -- このヘルプテキストを出力\n"
  + "pt.moveTo(x, y) -- 位置を変更\n"
  + "pt.toStringo() -- 文字列化\n"
  + "Point.norm(pt) -- (0, 0) からの距離を求める\n"
;

Point() をnewなしで呼び出すと、ヘルプテキストを出力します。そのヘルプテキストは、Pointオブジェクトのhelpプロパティに保存しています。

試してみる

今までに出てきたコード断片をまとめましょう。

function Point(x_, y_) {
  if (!(this instanceof Point)) {
    return Point.help;
  }
  this.x = x_ || 0;
  this.y = y_ || 0;
}

Point.help = 
  "\n"
  + "new Point(x, y) -- 新しい点を生成、x, y は省略可能\n"
  + "Point() -- このヘルプテキストを出力\n"
  + "pt.moveTo(x, y) -- 位置を変更\n"
  + "pt.toStringo() -- 文字列化\n"
  + "Point.norm(pt) -- (0, 0) からの距離を求める\n"
;

Point.prototype.moveTo = function(x, y) {
  this.x = x;
  this.y = y;
};

Point.prototype.toString = function() {
  return "(" + this.x + ", " + this.y + ")";
};

Point.norm = function(pt) {
  return Math.sqrt(pt.x*pt.x + pt.y*pt.y);
};

firebugのコンソールで試してみると次のようになります。


>>> Point()
"
new Point(x, y) -- 新しい点を生成、x, y は省略可能
Point() -- このヘルプテキストを出力
pt.moveTo(x, y) -- 位置を変更
pt.toStringo() -- 文字列化
Point.norm(pt) -- (0, 0) からの距離を求める
"

>>> var pt = new Point()
undefined

>>> pt.toString()
"(0, 0)"

>>> pt.moveTo(3, 4)
undefined

>>> Point.norm(pt)
5