関数とは、ひとまとまりの処理に名前をつけたもの
JavaScript でロジックのかたまりに名前をつけて使い回したいときは、必ず関数を書きます。もっとも昔からある素直な書き方が 関数宣言(function declaration) で、function キーワードのあとに関数名を続ける形式です。
これを前から順に読んでいくと、こうなります。
functionは、関数宣言の始まりを示すキーワード。greetは関数名。(name)は仮引数リスト。関数が受け取る入力です。- 波かっこで囲まれたブロックが関数の本体。
greet("Ada")は 呼び出し 。nameに"Ada"を束縛した状態で、JavaScript が本体を実行します。
関数は定義しただけでは動きません。呼び出して初めて実行されます。
仮引数と実引数
仮引数(parameter) は関数を定義するときに書く名前で、 実引数(argument) は呼び出すときに渡す値のことです。日常会話ではほぼ同じものとして扱われますが、エラーメッセージを読むときにはこの区別が効いてきます。
ここで base と exponent は仮引数(パラメータ)、2 と 10 は**実引数(アーギュメント)**です。JavaScript は渡された順番どおりに束縛していきます。つまり、1 番目の実引数は 1 番目の仮引数へ、2 番目は 2 番目へ、という具合ですね。
他の言語と違って、JavaScript は引数の数が合わなくても文句を言いません。足りない分は undefined になり、多すぎる分は黙って無視されます。柔軟ではあるものの、ハマりどころにもなりがち。デフォルト引数や残余引数(rest parameters)については後の章で扱います。
return で値を呼び出し元に返す
console.log は出力に書き出すだけですが、return は呼び出し元に値を返してくれるので、その値をそのまま使えます。
return を書かないと、関数は undefined を返します:
return が実行された時点で、関数はそこで即終了します。メインの処理に入る前に例外的なケースを弾きたいときは、早期リターン(early return)で抜けるのが定石です。
アーリーリターンを使えば、本体が if/else のピラミッドにならずに済みます。
巻き上げ(ホイスティング):定義する前に呼び出せる
これは他の言語から来た人が本当に驚くポイントです。関数宣言は 巻き上げ(ホイスティング) されます。つまり JavaScript はコードを実行する前に、関数宣言をスコープの先頭へ移動してくれるのです。そのため、宣言より前の行で関数を呼び出しても普通に動きます。
このコードは問題なく動きます。JavaScriptエンジンは実行前にスコープをスキャンして square を関数として登録し、それから文を上から順に実行していくからです。
これは関数宣言と、それ以外の関数の書き方(関数式やアロー関数)との間にある、実際の挙動の違いです。後者は変数と同じようにホイスティングされるので、名前 は認識されるものの、値はその行に到達するまで代入されません。この違いについては、このあと詳しく見ていきます。
とはいえ多くのスタイルガイドでは、関数は呼び出す前に定義することを推奨しています。巻き上げ(ホイスティング)はあくまで安全網であって、そこに頼る書き方をすべきではありません。
関数宣言と関数式の違い
関数宣言 (function declaration) はそれ自体が独立した文になります。一方で 関数式 (function expression) は値として扱われる位置、つまり代入の右辺などに書かれるのが一般的です。
どちらも呼び出し可能な関数を作れますが、違いは次のとおりです。
- 関数宣言は丸ごと巻き上げ(ホイスティング)されます。一方の関数式は、代入される変数のホイスティングルールに従います(
constやletはその行が実行されるまで一時的死角(TDZ)にいます)。 - 宣言には必ず名前が必要です。関数式は無名でも書けますが、名前を付けておくとスタックトレースで追いやすくなります。
- 関数宣言はどこにでも書けるわけではありません。strict モードでは
ifブロック内のfunction foo() {}はエンジンによって挙動がバラバラなので、そういう場所では関数式を使いましょう。
ファイルのトップレベルに置くヘルパー関数なら、関数宣言のほうが読みやすいことが多いです。引数として渡す関数や、プロパティに代入する関数には、関数式(もしくはアロー関数)を使うのが一般的です。
命名とスタイル
関数名は、その関数が何をするかの「約束」です。少し手間をかけてでも分かりやすい名前を選んでおくと、誰かがそのコードを初めて読むそのときに、ちゃんと元が取れます。
押さえておきたい命名ルールをいくつか挙げておきます。
- 関数名は動詞にします。たとえば
fetchProfile、computeTotal、sendEmailといった具合です。 - 表記は
camelCaseで。これが JavaScript の慣習です。 - 真偽値を返す関数は
is、has、canで始めることが多いです。isValid、hasAccess、canEditなどですね。
こうしておくと、呼び出し側のコードがそのまま英文のように読めるようになります。
実践的なサンプルコード
ここまで見てきた引数、早期 return、わかりやすい関数名を組み合わせて、ひとつの例にまとめてみましょう。
宣言ひとつ、ガード節ひとつ、そして明快な return。この形さえ押さえておけば、実際に書く関数のほとんどはカバーできます。
次はアロー関数
関数宣言が基本の道具であることは間違いありませんが、モダンな JavaScript ではもっと短い書き方——アロー関数——が頻繁に使われます。特にコールバックや 1 行で済む処理では主役と言ってもいいでしょう。見た目も違えば this の扱いも違う、そんなアロー関数を次のページで取り上げます。
よくある質問
JavaScriptで関数を宣言するにはどう書けばいい?
functionキーワードのあとに関数名、()に引数、{}に処理本体を書きます。例:function greet(name) { return 'Hi, ' + name; }。呼び出すときはgreet('Ada')のように名前に()を付けて実行します。
関数の巻き上げ(ホイスティング)とは?
関数宣言はコードが実行される前にスコープの先頭に巻き上げられるので、定義より上の行から呼び出してもちゃんと動きます。ただしこの挙動が効くのはfunction宣言だけ。letやconstに代入した関数式やアロー関数は同じようには巻き上がらないので注意してください。
関数宣言と関数式の違いは?
関数宣言はfunction greet() {}のように単独の文として書け、巻き上げの対象になります。一方の関数式はconst greet = function() {}のように変数へ代入する形で、変数の巻き上げルールに従います。宣言は必ず名前を持ちますが、関数式は無名で書かれることが多いのも違いのひとつです。