SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

JavaScriptによるオブジェクト指向プログラミング

JavaScriptの関数とメソッド

JavaScriptによるオブジェクト指向プログラミング 第2回


  • このエントリーをはてなブックマークに追加

このシリーズは、JavaScript(ECMAScript)における概念と言語構造の理解を助けるためのドキュメントです。第2回目の本稿では、JavaScript(ECMAScript)における関数の基本的な性質について解説します。

  • このエントリーをはてなブックマークに追加

はじめに

 JavaScriptはオブジェクト指向言語です。しかし利便性のために、C言語などと同じようにグローバルな関数を定義し、構造化的な手法でプログラミングすることも可能です。

 この記事では、JavaScriptにおける関数の基本的な性質を見ていくと共に、関数の正体、および関数の使用方法について解説していきたいと思います。

 なお、この記事はJavaScriptの解説ですが、その内容は、標準仕様のECMAScriptで扱われる範囲に基づいています。従って、同じくECMAScriptを元にしている言語(JScriptActionScript)でも通じる内容になっています。

対象読者

 プログラミングの基本的な知識、ならびにオブジェクトやメソッドと言った基礎的な概念については、ここでは解説しません。最低限、オブジェクト指向プログラミングについて理解をしている人を対象としています。

 この記事は、主に以下のような人を対象としています。

  • サーバサイドJavaを行っていて、JSPなどでJavaScriptの知識が必要になった人
  • JavaScriptをより深く使いこなしたい人
  • オブジェクト指向プログラミングを別のアプローチから捉えなおしてみたい人
  • JavaScriptでクラスを作る方法は知っているが、何故そのように書くとクラスを作れるのかわからない人

必要な環境

 ECMAScript 3rd editionの実装系。例えば、下記のような環境。

  • Microsoft Internet Explorer 5.5以上
  • Firefox 1.0以上

目次

  1. 関数もオブジェクトである
  2. メソッドもプロパティである
  3. 無名関数の定義
  4. 高階関数
  5. 引数の秘密

関数もオブジェクトである

 JavaScriptの関数はオブジェクトの一種です。つまり、定義されたプログラムを実行するだけでなく、メソッドやプロパティを持ち、またそれらを操作する事ができます。

 とりあえず、JavaScript関数の定義法を見てみましょう(List2.1)。

List2.1 function文での関数定義
function myFunc(arg)
{
    alert(arg);
}

myFunc("hoge");    // hoge と表示される。

 引数をダイアログで表示する簡単な関数です。

 関数がオブジェクトである、というなら、コンストラクタを用いて関数を定義する事ができるのでしょうか? できます。コンストラクタとnew式を用いて、List2.1と同じ関数を定義したのが以下のコードです(List2.2)。

List2.2 Functionコンストラクタでの関数定義
var myFunc = new Function("arg", "alert(arg);");

myFunc("hoge");    // hoge と表示される。

 List2.1とList2.2は全く同じことを表しています(*1)。

*1
 厳密に言えば違いはあります。その違いについては後の回にて解説します。

 ここで注目して欲しいのは、List2.2において、myFuncvarで宣言されている、つまりmyFuncは変数である、という事です。これが何を表すのかと言うと、関数は、一つ一つに名前が付けられるのではなく、ただ変数に関数オブジェクトの参照を代入するだけである、という事です。

 したがって、関数を他の変数に代入したりすることができるのです(List2.3)。

List2.3 関数の代入
function funcA() 
{
    alert("関数A");
}

var funcB = funcA; // 関数の代入

funcB();           // 関数A と表示される。

 関数を実行するためには、その関数オブジェクトを参照している変数に括弧をつけることで、実行する事ができます(*2)。

*2
 関数オブジェクトではない変数を実行しようとすると、当たり前ですがエラーになります。

 関数オブジェクトの参照を上手く使うことで、C言語で関数ポインタを利用するようなコードも簡単に書くことができます。

メソッドもプロパティである

 さて前節で、関数はオブジェクトであり、関数名はただの変数名だということを解説しました。

 それでは、オブジェクトのメソッドはどうなのでしょうか。

 実はこれも関数オブジェクトです。つまり、オブジェクトのメソッドというのは、単に関数オブジェクトの参照が代入されているプロパティだった、という訳です。

 もっと言ってしまえば、JavaScriptのオブジェクトメンバはプロパティしか存在しない、という事です。プロパティの中で関数オブジェクトの参照を持ったものをメソッドと呼ぶ、という訳です。

 従って、オブジェクトに新たなメソッドを定義したい場合、プロパティを定義するのと同じように、代入式を用いることでメソッドを定義する事ができます(List2.4)。

List 2.4 メソッドの定義
function func() 
{
    alert("関数です。");
}

var obj = new Object();
obj.method = func;    // 関数の代入

obj.method();

 やろうと思えば、プログラムのある時点ではメソッドで、別のある時点では、ただのプロパティである、というようなメンバを作る事もできます。コード的に読み難くなるので止めた方がいいとは思いますが。

無名関数の定義

 さて、オブジェクトのメソッドも代入式によって定義できる事を解説しました。しかし、毎回List2.4のように、関数の定義と代入を別々に行うのでは手間が掛かります。また、メソッドとしてしか使用するつもりの無い関数を、グローバルな関数で定義したままにしておくというのも、なにやら無駄な気がします。

 そこで、この節では関数を定義するもう1つの方法を解説したいと思います(*3)。無名関数の定義と呼ばれるこの方法では、関数の定義と代入を同時に行うことができ、またグローバルな関数が別途に定義される事もありません。

*3
 この方法はECMAScript初版以下の実装系では使用できません。

 具体的にコードを見てみましょう(List2.5)。

List2.5 無名関数の定義
var obj = new Object();

obj.method = function(arg)
{
    alert(arg);
}

obj.method("テスト");    // テスト と表示される

 何のことは無く、function文に関数名を書かないだけですね。この様に記述する事によって、グローバルな関数の定義を避け、直接プロパティに参照を代入する事ができるのです。

 オブジェクトのメソッドを定義する場合、大概はこの方法で記述する事になります。

 ところで、こんな表記を使わなくてもFunctionコンストラクタを使えばいいじゃないか、と思われる方がいらっしゃるかもしれません。正直なところ、List2.5のような場合では全くその通りです。

 ただ、厳密な話をするとfunction文を用いる場合とFunctionコンストラクタを用いる場合では、違いが出てくる時があります。この違いについては後の回にて詳細に解説したいと思いますので、ここでは、基本的にメソッドを定義する時は無名関数の定義を使う、という事を覚えておいて下さい。

高階関数

 前節にて関数はオブジェクトであり、変数やプロパティに代入する事ができるという事を解説しました。では、関数の引数として渡すことはどうでしょうか? もちろんこれも可能です。この様な引数に関数をとる事ができる関数の事を、高階関数といいます。関数型言語のLispSchemeといった言語ではお馴染みの機能なのですが、これもJavaScriptでは実現することができます。

 早速、具体例を見てみましょう(List2.6)。

List2.6 関数を引数に取る関数
// 配列の全要素に指定した関数を実行する
function eachArray(arr, func) {
    for (var i=0; i<arr.length; i++) {
        func(arr[i]);
    }
}

var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
eachArray(array, alert);

 List2.6を実行すると、配列の要素ごとにalertが実行され、計10回ダイアログが表示されます。この様な関数を引数に取る関数を使用することで、より簡潔なコードを記述することができる様になりますので、ぜひ覚えておきましょう。

引数の秘密

 前節までは、関数そのものについて見てきました。今度は、関数の使い方、引数に話を絞って見ていきたいと思います。

 function文で関数に引数を設定する事ができるのはご承知の事だと思います。それでは関数の実行時に、この宣言と一致しない引数で実行した場合、どのようになるのでしょうか?

 JavaやCといった言語では、関数(メソッド)のシグネチャ通りに引数を渡さなければ、コンパイルエラーになってしまいます(*4)(List2.7)。

*4
 正確には、呼び出しに一致するメソッドを見つけられなかった、というエラーになります。
List2.7 Javaでのシグネチャに一致しないメソッド呼び出し
class MyClass {
    public void method(Object arg1, Object arg2) {
        System.out.println(arg1.toString());
        System.out.println(arg2.toString());
    }
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.method();        // コンパイルエラー
    }
}

 ところが、JavaScriptではSyntaxErrorになるどころか普通に実行されてしまいます(List2.8)。

List2.8 JavaScriptでのシグネチャに一致しない関数呼び出し
function myFunc(arg1, arg2) {
    alert(arg1 + ":" + arg2);
}

myFunc("arg1", "arg2");                // 通常の呼び出し
myFunc();                              // 引数の数が足りない
myFunc("arg1", "arg2", "arg3");        // 引数の数が多い

 JavaScriptでは、関数を実行する時の引数の数は厳密に扱われません。引数の数が足りなければ、足りない引数はundefinedとして扱われます。

 では、引数の数が多かった場合、渡された値はどこへ行ってしまうのでしょうか? 無視され消えてしまうのでしょうか?

 実は、シグネチャよりも多くの引数を渡された場合でも、それらの値を扱う事ができます。それを可能にするのがargumentsオブジェクトです。

 関数の中では暗黙にargumentsというオブジェクトが生成されます。このオブジェクトが引数で渡された値を保持しているのです。

 argumentsオブジェクトはArrayインスタンス と同じように添え字で配列としてアクセスする事ができ、arguments[n-1]が第n引数を表します(List2.9)。

List2.9 argumentsオブジェクト
function myFunc(arg1, arg2) {
    // arguments[0] は第一引数を指すので true
    alert(arg1 == arguments[0]);
    // シグネチャに無い 第四引数にアクセスする事ができる
    alert(arguments[3]);
}

// 二回目のアラートは arg4 と表示される。
myFunc("arg1", "arg2", "arg3", "arg4");

まとめ

 今回は、以下のような関数の基本的性質とその使用方法を学びました。

  • 関数はオブジェクトである。
  • メソッドというのは、関数オブジェクトの参照を保持している、ただのプロパティである。
  • 無名関数という関数の定義方法がある。
  • 関数を引数に渡すことができる。
  • argumentsオブジェクトという引数を表すオブジェクトが存在する。

 次回は、JavaScriptにおけるプロトタイプについて解説します。

参考資料

 本稿は、Starry Night 『JavaScript講座』にて公開したものです。連載を再開するにあたり、再編集を行いCodezineに寄稿いたしました。

修正履歴

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加
JavaScriptによるオブジェクト指向プログラミング連載記事一覧

もっと読む

この記事の著者

中村 学(ナカムラ マナブ)

イー・デスク株式会社 開発部 部長。業務ではJava/PHPによるWebアプリケーション開発を行っている。学生時代はユーザインターフェイスデザインを専攻し、アクセシビリティ、ユーザビリティの観点からスタイルシート、JavaScript等の技術に造詣を深める。最近はRubyとLispに注目している。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/221 2005/12/02 12:37

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング