一章第四回 オブジェクトと関数
このページの最終更新日:
関数もオブジェクトだ
「オブジェクト」について多少わかってきたので、ここで関数という存在を見直してみましょう。
実は、基本的にJavaScriptでは全てのものがオブジェクトかプリミティブです。関数はどちらに入るのでしょう。
実は関数もオブジェクトです。ただ、すごく特殊なオブジェクトであるのは分かると思います。「()
」をつけると呼び出すことができるというのは関数だけがもつ特徴です。
実際、配列なんかよりももっと特殊なオブジェクトという位置づけです。しかし、オブジェクトであることは確かです。次のサンプルを見てみましょう。
function aaa(){
alert("test");
}
aaa();
alert(aaa);
アラートを表示するだけの関数aaaを作り、それを呼び出しています。よって、「"test"」のアラートが表示されます。
次の行でやっていることはなんでしょうか。aaa
を表示しています。
このaaa
というのは関数ですが、より正確にいうと、関数が入った変数です。つまり、関数定義というのは、新しい関数を作り、それを変数に入れる作業だったのです。(ここで、「作り」というキーワードに反応してもらえると、とてもよいです。オブジェクトを作るのと同様、関数も作るものです。)
そして、関数とはいえ、ある種のオブジェクトですから、好きなように扱うことができます。よって、alertで表示してみることもできるのです。そして、出るアラートには、関数aaaの中身が表示されています。
実は、関数をアラートで表示すると、その中身が表示されるようになっています。
もともとある関数の中身
次のようにすると、どうなるでしょうか。
alert(alert);
ややこしいですが、alert
というのは、もともとある関数でした。上で述べたように、関数といえども、所詮は変数に入った何かです。つまり、もともとある関数も、実は変数に入っています。alert
は、alertという名前の変数に入った関数です。
となると、先ほどと同様、変数alertが表す関数の中身を表示することができます。
これでalert関数の中身が見られるはずです。ブラウザによって違いがあるかもしれませんが、"function alert() { [native code] }"
と表示されると思います。
[native code]
というのは、この関数の実行はブラウザの内部処理で行われる(ので、JavaScriptで書かれた定義がない)ということを意味しています。
メソッド
次に、メソッドというものを解説します。そんなに難しいものではなく、プロパティのうち、関数であるもののことをメソッドといいます。つまり、プロパティの一種です。
var a = {};
a.abc = test;
a.abc();
function test(){
alert("test");
}
やっていることは難しくありません。1行目でaに{}
を代入していますが、これは「プロパティを1つも持たないオブジェクト」です。「プロパティ名 : 値
」の組が1つもないわけですね。
そして、その後a.abcにtestを代入しています。これは、当然aのプロパティabcです。testは関数であり、つまりオブジェクトでしたから、同じ関数がa.abcに代入されます。つまり、a.abc
を呼び出すのはtest
を呼び出すのと同じです。(詳しい方は、厳密には少し違うということをご存知だと思いますが、それはここでは解説しません。)
なので、次の行ではそれを呼び出しています。ちゃんと"test"
と表示されるのが分かります。
もともとメソッドの本当の意味はこんなところには無いのですが、それは今回は解説しません。オブジェクトにくっついてる関数をメソッドと呼ぶということは覚えておきましょう。
ちなみにこのように、関数定義は、使う場所より後ろに書いてあっても機能します。
関数式
関数を作る方法が、実は他にもあります。それは、次のようにする方法です。
var aaa = function (){
alert("test");
};
aaa();
1行目がaaaに何かを代入している文なのは分かると思いますが、代入されているものは
function (){ alert("test"); }
です。実はこれは、関数を定義するときに使うやつの、関数名を省略した形であるのがわかると思います。
これは、関数を新しく作る式です。オブジェクトリテラルがオブジェクトを作り、配列リテラルが配列を作るのと同じです。なので、これを関数リテラルと呼ぶことも可能かもしれませんが、そう呼ばれているのはあまり見ません。それよりは、関数式という呼び方が一般的なようです。
式というのは、より複雑な式の一部として使うことができるということです。例えば、次のように、関数呼び出しの引数に関数式を入れることもできます。
alert(function(){ alert("test"); });
実は、上で解説したメソッド作りなんかはこの記法が主流です。つまり、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のメソッドを作っています。
最近のJavaScript (ES2015)では、オブジェクトリテラル内でメソッドを定義する場合、次のように簡略化した記法があります。
var a = {
abc(){ alert("abc"); },
aaa(){ alert("aaa"); }
};
次のこれは分かるでしょうか。
var a = function(){
return function(){
alert("test");
};
};
a()();
これが正しく理解できれば、かなり力がついているといえるでしょう。
まず、関数aを見てみます。すると、関数aの中でまた関数式があるのが分かります。その前には「return
」とあります。戻り値を返すreturnです。
これは、関数aがfunction(){ alert("test"); }
という関数を返すだけの関数であることを表しています。この関数(正確には、この関数式によって作成される関数)を関数Aと呼びましょう。関数Aは、ただアラートを表示するだけですね。
ここで最後の謎の文を見てみると、まず「a()()
」のうち最初の「a()
」が処理されます。つまりは関数aを呼び出しているのです。
a()
の返り値は関数Aです。戻り値が返されるとそれに置き換わるので、この文は、擬似的にですが次のようになります。
関数A();
したがって、関数Aが呼び出されてアラートが表示されたというわけです。
もっとも、さすがにわかりにくすぎるので例えばこんなふうに書きます。
(a())();
最初のa()
が( 〜 )
で囲まれています。この括弧は計算の優先順位を高めるものです。(3+5)*2
のような場合の括弧と同じです。
今回の場合、もともとこの部分が先に計算されるので意味がないように思えますが、見た目少し分かりやすくなります。このように、見た目を分かりやすくするために括弧が使われることもあります。
無名関数
また、次のような書き方がそもそもできます。
(function(){ alert("test"); })();
ここでは、関数A(を返す関数式)がベタ書きしてあり、それを関数呼び出しの()
で呼び出しています。
このとき、関数Aは、関数式によって作成されたあと、変数に代入される間もなく呼びだされ、その役目を終えます。この関数は変数に入らなかったということは、名前がつけられていないということです。このような関数を無名関数などといいます。
関数については、今回さらっと紹介しましたが、さらに紹介するべき事柄が多くあります。特に、九章第五回で詳しく解説しています。