このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

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