uhyohyo.net

JavaScript初級者から中級者になろう

一章第四回 オブジェクトと関数

関数もオブジェクトだ

「オブジェクト」について多少わかってきたので、ここで関数という存在を見直してみましょう。

実は、基本的にJavaScriptでは全てのものがオブジェクトプリミティブです。関数はどちらに入るのでしょう。

実は関数もオブジェクトです。ただ、すごく特殊なオブジェクトであるのは分かると思います。「()」をつけると呼び出すことができるというのは関数だけがもつ特徴です。

実際、配列なんかよりももっと特殊なオブジェクトという位置づけです。しかし、オブジェクトであることは確かです。次のサンプルを見てみましょう。

function aaa(){
  alert("test");
}
aaa();

alert(aaa);
        

アラートを表示するだけの関数aaaを作り、それを呼び出しています。よって、「"test"」のアラートが表示されます。

しかし、次の行でやっていることはなんでしょう。なんとそのaaaを表示しています。そして、出るアラートには、関数aaaの中身が表示されています。

実は、関数をアラートで表示すると、その中身が表示されるようになっています。それより、アラートの引数がaaaだということは、aaaを変数のように使っているということです。実は、その通りaaaも変数だといっていいです。

実は、function 関数名(){ 〜 }のような表記では、「『関数名』という名前の関数をつくる」というように基礎第四回で解説しましたが、もっと正確には、関数(のオブジェクト)を作り、その関数への参照を関数名に代入するという処理をしているといえます。

つまり、普通のオブジェクトと同じように、変数aaaには関数への参照が入っていたのです。それに「()」を付けて呼び出す呼び出し方をすると、関数として呼び出せるというわけです。

もともとある関数の中身

上で関数を変数としてアラートで表示すると、中身を表示できることを解説しました。それでは、次の場合はどうでしょう。

alert(alert);

ややこしいですが、alert関数で、変数alertが表す関数の中身を表示しています。注意として、1つめ(左)のalertは関数呼び出しのalertで、2つめ(右)のalertは変数名としてのalertだということです。

これでalert関数の中身が見られるはずです。ちなみに、ブラウザによって違いがありますが、Operaではfunction alert() { /* source code not available */ }と表示されました。

中身があるべき部分に、コメントで「source code not available」と書いてあります。英語ですが、直訳すると「利用できないソースコード」となります。意味が分かりにくいですが、このalertはJavaScriptとは少し離れたところで処理をしていて、処理の中身はJavaScriptで書かれていないのです。基本的にもともとある関数は、自分で作った関数のように処理がJavaScriptでは書かれていません。ブラウザ本体がもつ機能とJavaScriptの架け橋であるといえます。JavaScriptとは違うため、JavaScriptで表せないから「利用できない(見ることができない)ソースコード」なのです。

しかし、もともとある関数でまた特別だとはいっても、オブジェクトであることは確かです。

メソッド

メソッドというものを解説します。そんなに難しいものではなく、プロパティのうち、関数であるもののことをメソッドといいます。つまり、プロパティの一種です。

var a = {};
a.abc=test;
a.abc();

function test(){
    alert("test");
}

やっていることは難しくありません。1行目でaに{}を代入していますが、これは「プロパティを1つも持たないオブジェクト」です。「プロパティ名 : 値」の組が1つもないわけですね。

そして、その後a.abcにtestを代入しています。これは、当然aのプロパティabcです。このabcの参照先はtestの参照先と同じですから(変数testに入っている関数オブジェクトへの参照をa.abcにも代入したと考えてください)、abcはメソッドであるといえます。

次の行ではそれを呼び出しています。正常に呼び出せていることが分かります。

もともとメソッドの本当の意味はこんなところには無いのですが、それは今回は解説しません。オブジェクトにくっついてる関数をメソッドと呼ぶということは覚えておきましょう。

ちなみこのように、関数定義は、使う場所より後ろに書いてあっても機能します。

関数式

関数を作る方法が、実は他にもあります。

var aaa = function (){
    alert("test");
};

aaa();
        

1行目がaaaに何かを代入している文なのは分かると思いますが、代入されているものは

function (){ alert("test"); }

です。実はこれは、関数を定義するときに使うやつの、関数名を省略した形であるのがわかると思います。

この形にすると、[ 〜 ]の配列や{ 〜 }のオブジェクトと同じように、変数に代入したりできます。このように作られる関数のことを関数式といいます。式というのは、演算子のオペランドになったり関数の引数になったりできるものだということです。

実は、上で解説したメソッド作りなんかはこの記法が主流です。つまり、こういうことです。

var a = {};
    a.abc = function(){
    alert("test");
};
        

注意するべきなのは、あくまで代入文なので、function(){ 〜 }の後には「;」を付けるべきだという点です。通常の関数の定義ではつけていませんでしたね。

また、次のようなこともできます。

var a = {
    abc : function(){ alert("abc"); },
    aaa : function(){ alert("aaa"); }
};
a.abc();
a.aaa();
        

最初にオブジェクトを作る時点で、既にabcとaaaのメソッドを作っています。

次のこれは分かるでしょうか。

var a = function(){
    return function(){
        alert("test");
    };
};
a()();
        

これが正しく理解できれば、かなり力がついているといえるでしょう。

最後で「()」が2回つく謎の関数呼び出しが行われていますが、実はこれは新しい記法というわけではありません。

まず、関数aを見てみます。すると、関数aの中でまた関数があるのが分かります。その前には「return」とあります。戻り値を返すreturnです。

これは、関数aが

function(){
            alert("test");
            }

を返すだけの関数であることを表しています。返されるほうの関数は、ただアラートを表示するだけですね。

ここで最後の謎の文を見てみると、まず「a()()」のうち最初の「a()」が処理されます。つまりは関数aを呼び出しているのです。

戻り値が返されるとそれに置き換わるので、この文は次のようになります。

function(){
              alert("test");
              }();

最後の「();」がもともとあったやつで、その前のfunction(){ 〜 }が返されたやつです。

function(){ 〜 }が実質的に返すのは参照なので、参照();のような文に実質的になっていることがわかります。

したがって、その参照が呼び出されてアラートが表示されたというわけです。

もっとも、さすがにわかりにくすぎるので例えばこんなふうに書きます。

(a())();

最初のa()( 〜 )で囲まれています。

今度はa()が何かの引数になってしまったようにも見えますが、実はこの(〜)は関数呼び出しの()ではありません。これは優先順位を高めるためのものです。

例えば、掛け算より足し算を先にしたいとき、「(3+5)×2」のようにします。括弧の中が先に計算されるのでした。これと同じはたらきです。

今回の場合、もともとこの部分が先に計算されるので意味がないように思えますが、見た目少し分かりやすくなります。このように、見た目を分かりやすくするために括弧が使われることもあります。

無名関数

また、次のような書き方がそもそもできます。

(function(){ alert("test"); })();

実は、さっきのサンプルでa()が処理され終わった後と同じ形です。

右の()によって、

(function(){ alert("test"); })

が呼び出されます。このとき、呼び出された関数は名前がつけられていません。このような関数を無名関数などといいます。function(){ 〜 }で作られる関数は無名関数であるといえます。

他の変数に代入してしまえば無名関数はその変数を通して名前を持つことになりますが、今回の場合、代入もしていないので本当に無名です。このようなものだけを特に無名関数という場合もあります。

名前を持たないので、今回()で呼び出した後は、二度とこの関数には触れなくなってしまいます。この関数を参照する変数が、つまりこの関数にたどりつくための名前が無いわけです。

それなら関数にする意味もないかもしれませんが、大規模な処理をする場合など、関数の中でローカル変数を使えることが役に立つ場合もあります。たくさん変数を使う処理をすると、他のところで使っている変数とかぶってしまうかもしれません。しかし、新しい関数をつくってその中でローカル変数を作って処理すれば、外には影響を与えないのです。

関数については、さらに紹介するべき事柄が多くあります。特に、九章第五回で詳しく解説しています。