いよいよ本論に突入。まずは関数についてまとめる。JavaScriptでは関数もオブジェクトであり、柔軟な構文が用意されている。
JavaScriptが、いま注目を浴びている。
前回も紹介したように、JavaScriptが復権を果たした背景にはさまざまな要因が考えられるが、その契機ともなったキーワードを1つだけ挙げるとするならば、「Ajax(Asynchronous JavaScript And Xml)」を外すことはできないだろう。Ajaxという技術の登場によって、JavaScriptによるリッチなユーザー・インターフェイス構築の可能性が示されたことで、JavaScriptの価値は確実に見直されつつある。
本連載は、JavaScript復権のいまこの時代に求められる、JavaScriptという言語への理解を再確認しようというものだ。連載初回ではまずJavaScriptの苦渋の10年間を振り返りながら、JavaScriptという言語にまとわりつく誤解の払拭を試みた。JavaScriptという「不遇な」言語にこれまで向けられがちであった3つのマイナス・イメージについて、それらがあくまでイメージ先行の誤解であることを理解できたはずだ。そして、第2回である今回からは、いよいよ本論に入っていくことにしたい。
今回扱うテーマとなるのは「関数」だ。後述するように、JavaScriptにおける関数とはそれ自体が「オブジェクト」であり、変数やオブジェクトのメンバとして格納したり*1、あるいは、引数としてほかの関数に引き渡したりすることも可能だ。
オブジェクトといって分かりにくければ、関数はJavaScriptにおける「データ型」の一種であるといってもよいだろう。関数のこの特徴は、JavaScriptプログラミングの柔軟性を支えるものであり、同時に、(JavaScriptに不慣れな開発者にとっては)まず初めのとっつきにくさを感じさせる一因でもある。
今回は、この関数の基本的な構文とその特徴、そして、実際のプログラミングでの実用例について解説していく。
*1 オブジェクトのメンバとして格納された関数のことを「メソッド」という。メソッドについては、本連載第4回であらためて紹介する予定だ。
JavaScriptで関数を定義するには、大きく3つの方法がある。
本節では、まずこれら3つのアプローチでの基本的な構文と、それぞれの構文における挙動の違いについて解説していく。
■function文による定義
JavaScriptで関数を定義する場合の最も基本的な構文だ。いまさらかもしれないが、確認の意味も込めて、function文による関数定義の構文を示しておくことにしよう。
function 関数名([引数1 [, 引数2 [, ……]]) {
[関数内で実行される任意の命令……]
}
キーワード「function」の後方に、関数名、そして引数を丸カッコでくくって指定する必要がある。なお、関数名は一般的な文字列や式ではなく、識別子(名前)である必要がある。識別子については、本項末尾のコラムでまとめているので、併せて参照するとよい。
また、関数の本体は、必ず中カッコ({ })でくくること。if/whileなどの命令ブロックでは、その配下に文が1つしかない場合、
if (x == 1) alert('変数xは1です。'); // 中カッコの省略
のように中カッコを略記することが可能であるが*2、関数定義では配下の文が1つであっても「中カッコは省略できない」ので注意すること。
*2 ただし、if/whileなどの命令ブロックでも、ブロックの範囲を明確にするという意味で、中カッコは常に記述するのが好ましい。
以下に、具体的な関数の例を示してみよう。以下のadd関数は、引数x、yで与えられた数値を加算し、その結果を戻り値として返すものだ。なお、関数から呼び出し元に戻り値を返すのはreturn文の役割である。関数が戻り値を持たない場合、return文は省略することもできる。
window.alert(add(5, 7)); // 「12」を表示
function add(x, y) {
return x + y;
}
これはごく基本的な関数の例でもあり、特筆すべき点はないように見えるかもしれない。しかし、次のような例ではどうだろう。
function add(x, y) {
return x + y;
}
window.alert(add(5, 7)); // 「12」を表示
add = 0; [A]
window.alert(add); // 「0」を表示 [B]
window.alert(add(5, 7)); // 「関数を指定してください」エラーが発生 [C]
恐らく、このコードを見た多くの方が違和感を持つはずだ。add関数と同名の変数が定義されたことに問題があるならば[A]でエラーが発生するはずだし、変数を評価する際に関数と変数が識別できないことに問題があるならば[B]でエラーが発生するはずだ。しかし、エラーが発生するのは[C]である。
この挙動は、JavaScriptにおける関数の重要な性質を示している。結論から述べるならば、(冒頭でも述べたとおり)JavaScriptにおける関数とはオブジェクトであり、関数を定義するのは「<関数名>という名前(ここではadd)の変数に関数オブジェクトを格納している」のと同意であるということだ。
この視点でもう一度コードを見直してみると、JavaScriptの動作が明確になるはずだ。最初の段階では、変数addには関数オブジェクトがセットされている。これが[A]の代入式で上書きされ、数値0がセットされる。従って、[B]の時点では変数addの最新の情報である0が表示されるが、その次の[C]では数値を「add(5, 7)」のような式で評価しようとしたためにエラーが発生した ―― とまあ、こういうわけだ。
これをもっとあからさまに確認しているのが、以下のコードだ。
function add(x, y) {
return x + y;
}
window.alert(add);
ここでは、以下の画面のように、関数定義がそのままダイアログ表示されることが確認できるはずだ。ここからも、function文によって変数に関数定義が格納されていることが理解できるだろう。
ただし、function文による関数定義が、いわゆる「=」演算子による代入式とは異なる点もあるので注意が必要だ。例えば、以下のようなコードを見てみよう。
window.alert(add(5, 7)); // 「12」を表示 [A]
function add(x, y) {
return x + y;
}
「関数定義とは変数定義である」という理解と照らし合わせてみると、これまた、直感的には理解しがたいコードだ。function文がそのまま変数を定義しているならば、[A]の時点ではまだadd関数は定義されていないのでエラーが発生しなければならない。
しかし、実際にはそうはならない。
これは、厳密には、function文は動的に実行される文ではなく、静的な構造を宣言するためのキーワードであるためだ。「静的な構造を宣言」とはどういうことかというと、要はコードが解析/コンパイルされるタイミングで、function文は関数を定義してしまうということだ。従って、実行時にはすでにコード内の構造としてadd関数が登録されているものとして、どこからでもadd関数を呼び出すことができる。
Copyright© Digital Advantage Corp. All Rights Reserved.