JavaScriptの関数宣言と関数式の違いをわかりやすく解説
生徒
「JavaScriptで関数を作る方法っていくつかあると聞きました。関数宣言と関数式って何が違うんですか?」
先生
「どちらも関数を作る方法ですが、使い方や特徴が少し違います。まずは基本を押さえて、わかりやすく説明しますね。」
生徒
「お願いします!初心者でもわかるように教えてほしいです。」
先生
「それでは、具体的な例を交えて見ていきましょう!」
1. 関数宣言とは?
関数宣言は、JavaScriptで関数を作る一番基本的な方法です。function キーワードを使って名前をつけ、処理の中身を書きます。例を見てみましょう。
function greet() {
console.log("こんにちは!");
}
このコードは greet という名前の関数を作っています。呼び出すと「こんにちは!」と表示されます。
関数宣言の特徴の一つは、関数を呼び出すコードより前に書かれていなくても、実行時に使えることです。
2. 関数式とは?
関数式は、関数を変数に代入して作る方法です。変数に関数を入れるイメージです。例を見てみましょう。
const greet = function() {
console.log("こんにちは!");
};
この場合、greet は変数ですが、中身は関数です。呼び出すときは関数宣言と同じように greet() と書きます。
ただし、関数式の場合は、関数を作ったあとにしか使えません。つまり、関数式の前で greet() を呼ぶとエラーになります。
3. 関数宣言と関数式の大きな違いは「巻き上げ(ホイスティング)」
JavaScriptでは、関数宣言は「巻き上げ(ホイスティング)」という仕組みで、プログラムのどこで呼んでも使えます。これは関数宣言が実行前にメモリに読み込まれるためです。
例えば、次のコードはエラーになりません。
sayHello();
function sayHello() {
console.log("こんにちは!");
}
一方、関数式は巻き上げされません。変数に代入されるまでは使えないため、次のコードはエラーになります。
// エラーになるコード
sayHello();
const sayHello = function() {
console.log("こんにちは!");
};
4. もう少し身近な例えで理解しよう
関数宣言は「朝の準備を全部まとめたタイムテーブル」を先に用意しておくイメージです。だから朝でも昼でも、そのスケジュールをすぐに見て使えます。
関数式は「その日の予定を書いたメモ」を手に持っているようなもの。メモを持つまでは予定を確認できません。持ったあとなら見られます。
この違いが「巻き上げ(ホイスティング)」のイメージに当たります。
5. どちらを使えばいいの?
初心者のうちは関数宣言から学ぶのがおすすめです。わかりやすく、どこで呼んでも使えるので扱いやすいです。
関数式は少し慣れてきたら使うと良いでしょう。特にアロー関数などと組み合わせて使うことが多いです。
6. 関数宣言と関数式のまとめ
- 関数宣言は
function 関数名() { }の形で書く。巻き上げがあるためどこでも呼べる。 - 関数式は関数を変数に代入する形。巻き上げがなく、代入前は呼べない。
- 使いやすさやコードの書き方に応じて使い分ける。
まとめ
ここまでJavaScriptにおける「関数宣言」と「関数式」の基本的な定義から、その決定的な違いである「巻き上げ(ホイスティング)」の挙動について詳しく解説してきました。JavaScriptを学び始めたばかりの頃は、どちらの書き方を使っても同じように動くため、なぜ複数の書き方が存在するのか疑問に思うことも多いでしょう。しかし、プログラムの規模が大きくなり、複雑なロジックを組むようになると、この「宣言のタイミング」や「スコープ」の理解がデバッグの効率を大きく左右することになります。
関数宣言と関数式の構文をおさらい
改めて、それぞれの構文をコードで比較してみましょう。関数宣言は「独立した構文」として存在し、関数式は「値(式)の一部」として変数に格納されます。
// 関数宣言:独立して定義される
function showMessage(text) {
console.log("メッセージ: " + text);
}
// 関数式:変数に代入して定義される
const displayMessage = function(text) {
console.log("表示: " + text);
};
このように、関数式の場合は最後にセミコロン「;」を付けるのが一般的です。これは、変数への代入文(式)としての性質を持っているためです。一方、関数宣言にはセミコロンは不要です。こうした細かい作法の違いも、コードの読みやすさに影響を与えます。
実行結果の違いを確認する
巻き上げの有無が、実際のコンソールにどのような影響を与えるか、具体的な実行例で見てみましょう。関数宣言の場合は、定義より前で呼び出しても正しく実行されます。
// 関数宣言なら呼び出しが先でもOK
calculateSquare(5);
function calculateSquare(number) {
console.log(number * number);
}
上記のコードを実行すると、次のような結果が得られます。
25
しかし、関数式で同じことを行おうとすると、JavaScriptエンジンは「まだその変数は初期化されていません」とエラーを吐き出します。
// 関数式の場合、代入より前の呼び出しはエラー
// try-catchでエラーを捕捉する例
try {
calculateCircleArea(10);
const calculateCircleArea = function(radius) {
console.log(3.14 * radius * radius);
};
} catch (e) {
console.log("エラーが発生しました: " + e.message);
}
実行結果は以下のようになり、プログラムが停止してしまいます。
エラーが発生しました: Cannot access 'calculateCircleArea' before initialization
モダンな開発での使い分け
現代のフロントエンド開発(ReactやVue.jsなど)では、`const` を使った関数式、あるいはその発展形である「アロー関数」が多用される傾向にあります。これは、関数の再代入を防ぎ、プログラムの意図を明確にするためです。しかし、大規模なライブラリの内部構造や、あえて巻き上げを利用してコードの可読性を上げる(重要な処理を上に、詳細な定義を下に書く)手法を取る場合には、関数宣言が有効な場面もあります。
生徒
「先生、まとめを読んでさらによくわかりました!関数宣言は『いつでもどこでも呼べる魔法の箱』で、関数式は『箱を作ってからじゃないと中身が取り出せない慎重な箱』という感じですね。」
先生
「その例え、とてもいいですね!特に『慎重な箱(関数式)』を使うと、プログラムの実行順序を強制できるので、予期せぬエラーを防ぎやすいというメリットもありますよ。」
生徒
「なるほど。ところで、さっきのコードで const を使っていましたけど、もし var を使ったらどうなるんですか?」
先生
「鋭いですね! var を使った場合、変数名自体は巻き上げられますが、中身(関数の処理)は巻き上げられません。その結果、呼び出し時に『それは関数ではありません(undefinedです)』という別のエラーが出てしまうんです。今の開発では const や let を使うのが基本なので、まずは関数式の『呼び出しは定義の後』というルールを徹底しましょう。」
生徒
「わかりました!関数の書き方一つで、プログラムの挙動や安全性が変わるなんて面白いです。今度からコードを書くときは、どちらの形式がふさわしいか考えて使い分けるようにしてみます!」
先生
「その意気です!使い分けに正解はありませんが、チームやプロジェクトのルールに合わせて最適な方を選べるようになると、さらにステップアップできますよ。頑張りましょうね。」