【コラム】

そろそろきっちりJavaScript

2 無名関数についてもう少し考える

    富田陽介  [2007/03/05]

    無名関数のメリット

    前回の記事では、無名関数を関数リテラルに割り当てるという操作を紹介した。なぜ、このような記法により関数を定義するのだろうか。

    まず、無名関数を採用するメリットには、「命名する必要の無い関数をシンプルに記述できる」「(複数ファイルのJavaScriptライブラリを読み込んでいるときなどにおいて)、既存の関数/変数名との衝突を回避できる」という点が挙げられる。

    前回の冒頭で述べた通り、JavaScript関連ライブラリが数多く存在し、多くのシーンでそれを活用している今日、既存のライブラリやソースコードと名前が衝突しない事がどれだけ重要であるかは想像に難くないだろう。

    それでは、命名する必要の無い関数とはどのようなものだろうか。また、無名関数だと変数名の有効範囲はどのように決まっているのだろうか。ここで、前回に引き続きFirebugで以下のコマンドを実行し、動作を確認してみよう。

    例1

    /* 命名する必要の無い無名関数をwindow.onclickイベントハンドラにバインド */
    /* コマンド実行後にウィンドウ内をクリックしてダイアログが表示される事を確認しよう */
    >>> window.onclick = function() { var x="JavaScript!"; alert("Hello, " + x); }

    例2

    /* 無名関数内での変数定義が外部の(グローバルな)変数を上書きしていないことを確認しよう */
    >>> var y=2;
    >>> var mtof = function(x) { var y=3.2808399; return x*y; }
    >>> mtof(3);
    9.8425197
    >>> y
    2
    /* 関数定義をしない、単なるブロック処理だと、変数は上書きされてしまう */
    >>> { var y=4; }
    >>> y
    4

    例3

    /* 無名関数を定義し、その場で引数を与えて実行し、実行結果を変数に代入する */
    >>> var three_feet = function(x) { var y=3.2808399; return x*y; }(3)
    >>> three_feet
    9.8425197

    上記[例1]は、ウィンドウ内をクリックしたときのイベントハンドラプロパティに無名関数をバインドしている。クリックしたときの一連の処理の流れを定義しているだけなので、この関数に命名が不要であることは直感的に理解できるだろう。

    上記[例2]では、無名関数内で定義された変数yが、外部の変数を上書きしていないことが確認できる。しかしながら、単に {} で囲んだブロック内での処理においては、変数は上書きされてしまう。このように、変数の有効範囲(スコープ)は関数内に閉じられる。外部のライブラリや前処理に関係なく、独立した一連の処理を行いたい場合に無名関数が有効であることが理解できるだろう。

    [例3]は、さらに無名関数をその場で実行しているものだ。この書き方は、各種ライブラリの初期化処理などで多用されている。初期化処理(無名関数)内で多様な変数名が用いられたとしても、その外側で定義されている変数が影響を受けることは無い。

    余談だが、関数リテラルの記法(表現)は関数型言語にもしばしば登場する。Lispなどの関数型言語を学んだことのある読者は、ラムダ関数(ラムダ式あるいはラムダ計算)について知っていることと思う。(ラムダ計算とは、計算量理論や数値論理学の分野で登場する概念であり、関数をギリシャ文字ラムダ(λ)を用いて表記するものである。詳しくは専門書籍等を参照されたい)。JavaScriptにおける関数リテラルの表記方法は、関数型言語にも見られる、このラムダ関数の表記方法と同じものである。

    ラムダ計算の目的や意図するところをここで述べるのは本稿の主旨に外れてしまうため、この話題についてはここまで。ただ、JavaScriptにおいて、ラムダ関数表現が関数リテラルによって記述できることは、知っておいたほうがいいだろう。

    なお、JavaScriptでの関数表現には、この他に、以下のようなものもある。Firebugで実行し、動作を確認しておこう。この表現に関する詳細な説明は、今回は割愛するが、今後たびたび登場するものであるため、機会を見て紹介していきたい。

    例4

    /* 関数の中に関数を入れ子(ネスト)にして定義する */
    >>> function mtof(x) { function conv(x) { return x*3.2808399; } return conv(x); }
    >>> mtof(3)
    9.8425197
    /* 内側の関数に、外側からアクセスすることはできない */
    >>> conv(3);
    conv is not defined

    例5

    /* dollar関数 .. $() は document.getElementById() の別名として利用できる */
    /* 引数は配列でも受け取れる。多用されるため、覚えておいて損は無い */
    >>> document.write('<h1 id=\'myHeader\'>Hello!</h1>');
    >>> $('myHeader').style.color='red';
    "red"

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

    前項では、無名関数による定義のメリットについて触れた。それでは、(無名)関数を関数リテラルとして定義するシーンとはどのようなときか、改めて考えてみよう。ここからは、さらに一歩進んで、JavaScriptによるオブジェクト指向プログラミングについて紹介していく。

    JavaScriptでオブジェクト指向、とは馴染みの無い響きかもしれない。しかし、JavaScriptは立派なオブジェクト指向言語。関数型言語のようでいて、オブジェクト指向言語でもある。JavaScriptの面白さはこれからだ。

    JavaScriptでのオブジェクト指向プログラミングに触れる前に、まずJavaScriptでのオブジェクト型について確認しておこう。Mozilla Developer CenterのCore JavaScript 1.5 Guideによれば、オブジェクト型の定義は以下のようになっている。

    An object literal is a list of zero or more pairs of property names and associated values of an object, enclosed in curly braces ({}).

    端的に訳すなら、「プロパティ名とそれに対応づけられた値のペアのリスト」ということになる。JavaやC++などでいうオブジェクトのように、属性(フィールド)と操作(メソッド)という言葉が登場していない事を確認しておこう。

    さて、JavaScriptでのオブジェクトはJSON(JavaScript Object Notation)という表記法によって表現することができる。さっそく、Firebugで次のコマンドを実行し、オブジェクトを定義してみよう。

    /* プロパティ名と値のペアをコロン ":" で区切って定義する */
    >>> var Car = { 'mfr':'Toyota', 'model':'FunCargo' }
    >>> Car.mfr
    "Toyota"
    >>> Car.model
    "FunCargo"

    オブジェクトを定義し、プロパティにアクセスできることが確認できただろうか。しかし、これだけでは、まだオブジェクト指向という感覚からは程遠い。メソッドはどのように定義するのだろうか。そもそも、JavaScriptでオブジェクト指向ができると何が嬉しいのだろうか。次回以降、解説していこう。

    新着記事

    特設サイトの情報

      人気記事

      一覧

        イチオシ記事

        新着記事

        特別企画

        マイナビニュースマガジン