JavaScriptの関数式と関数宣言の違いとは?ホイスティングの仕組みを徹底解説
JavaScriptにおける関数宣言(function declaration)と関数式(function expression)の違いについて、具体例を交えながらわかりやすく解説します。
まず、関数式の例から見てみましょう。
// 関数式
let executeMe = function () {
console.log('Function Expression')
}
次に、関数宣言の例です。
// 関数宣言
function executeMe() {
console.log('Function Declaration')
}
一見すると、関数式と関数宣言はほぼ同じ働きをします。どちらの例でも executeMe() のように呼び出せば、コードブロック {..} 内に書かれた処理が実行されます。
しかし、両者には重要な違いがあります。それは「何をするか」ではなく、「どのように実行されるか」、つまりコードが読み込まれ・実行される「順序」に関わるものです。そしてこの違いは、実際の開発において大きな意味を持ちます。
たとえば、次のコードは問題なく動作します。
// 関数宣言を呼び出し
executeMe()
// 関数宣言
function executeMe() {
console.log('Function Declaration')
}
しかし、次のコードはエラーが発生して動作しません。
// 関数を呼び出し
executeMe()
// 関数式
let executeMe = function () {
console.log('Function Expression')
}
なぜ最初の例(関数宣言)は動作するのに、2番目の例(関数式)は動作しないのでしょうか。その理由を理解するには、JavaScriptにおける「ホイスティング(hoisting)」という概念を学ぶ必要があります。これを知れば、すべてが腑に落ちるはずです。
ホイスティング(Hoisting)とは
JavaScriptでは、コードは基本的に上から下へと順番に実行されます。通常、変数はスクリプト内で宣言された後でなければ使用できません。しかし関数宣言だけは例外で、ブラウザによってコード実行前に自動的にスクリプトの先頭へと引き上げられます。これが「ホイスティング」です。
つまりホイスティングとは、ブラウザのコードインタプリタが、関数宣言が呼び出される前に必ずその存在を把握(読み込み)している状態を指します。
そのため、関数宣言であればJavaScriptファイル内のどこからでも呼び出して実行できます。物理的にはファイルの後半に定義されている関数でも、順序的にはそれより前の行から呼び出せるのです。この点が関数宣言の特異な性質といえます。
関数宣言の場合
コードが実行される前に、ブラウザはJSファイル全体を読み込みます。関数宣言は実行順序の先頭へ移動(ホイスティング)されるため、インタプリタから見ると、先ほどのコードは実際には次の順序で実行されていることになります。
// 1. 関数宣言
function executeMe() {
console.log('Function Declaration')
}
// 2. 関数宣言の呼び出し
executeMe()
たとえファイル上では逆の順序で書かれていたとしても、です。
// 関数宣言の呼び出し
executeMe()
// 関数宣言
function executeMe() {
console.log('Function Declaration')
}
だからこそ、関数宣言の呼び出しを定義より前に書いても後ろに書いても、どちらでも正しく動作します。JavaScriptは常に、関数宣言を先に読み込んでから、その関数への呼び出しを実行するためです。
関数式はホイスティングされない
一方、関数式はホイスティングされません(事前に読み込まれません)。関数式は、インタプリタがその行に到達した時点で初めて読み込まれます。したがって、関数式が関数呼び出しよりも後ろ(順序的に)に記述されている場合、ブラウザのJavaScriptインタプリタはそれを実行できません。
つまり、次のコードは動作しません。
// 関数式を呼び出し
executeMe()
// 関数式
let executeMe = function () {
console.log('Function Expression')
}
このコードでエラーが発生するのは、ブラウザのインタプリタがまだ関数式の存在を把握(読み込み)していない段階で、関数 executeMe() を実行しようとしているためです。
コードの順序を入れ替えれば、今度は正しく動作します。
// 関数式
let executeMe = function () {
console.log('Function Expression')
}
// 関数式を呼び出し
executeMe()
このコードが動くのは、コードの順序が正しいからです。ブラウザは上から下へと読み進め、まず関数式を格納した変数 let executeMe の行を読み込み、その後に executeMe() で実行を求めるためです。
どちらを使うべきか?
重要なのは、関数宣言と関数式のどちらを選ぶかということではありません。どちらを選んでも、キャリアを左右するようなことにはなりません。むしろ大切なのは一貫性です。スタイルをひとつ決めたら、少なくとも同じプロジェクト内ではそれを貫きましょう。
さらに言えば、もし参加するチームが関数式を採用しているなら、自分の好みがどうであれ、チームの流儀に従うのが賢明です。コードベースの一貫性を保つことが何よりも優先されます。
関数式を使うメリットのひとつは、より厳格なコード構造が強制される点です。これによりコードの挙動が予測しやすくなります。一方、関数宣言は寛容で、スクリプト内のどこからでも呼び出せるため、複数の開発者が同じプロジェクトに携わる場合など、コード構造が不統一になりうる側面があります。
関数宣言を使う良いケース(議論の余地はありますが)としては、ある関数が別の関数を呼び出す構成になっていて、実行順序の混乱から関数式がどうしても正しく動作しないような場合が挙げられます。
-
iPhone XとiPhone 8の違いを徹底比較!サイズ・デザイン・カメラ・価格まで完全解説
iPhone 8、iPhone 8 Plus、そしてiPhone Xは、当時のiPhoneファミリーにおける最新モデルとして登場しました。いずれもフラグシップモデルにふさわしい先端機能を備えています。iPhone 8とその大型版のiPhone 8 Plusは、前世代のiPhone 7/7 Plusとデザインや構成がよく似ていますが、iPhone X(読み方は「テン」)はこれまでとはまったく異なる方向性を採用した意欲作です。 iPhoneの買い替えを検討している方の中には、「結局どちらを選べばいいのか?」と迷っている方も多いのではないでしょうか。この記事では、iPhone 8とiPhone X
-
マルウェアとウイルスの違いとは?正しい知識で守るセキュリティの基本
マルウェアとウイルスの違いは? マルウェア(Malware)とは、ホストシステムやそのユーザーに感染・被害をもたらす目的で作られた悪意のあるソフトウェア全般を指す総称です。コンピュータウイルスは、そのマルウェアの一種にすぎません。「すべての正方形は長方形だが、すべての長方形が正方形ではない」という関係と同じように、すべてのウイルスはマルウェアですが、すべてのマルウェアがウイルスというわけではありません。 つまり、「マルウェアとウイルスは同じものか?」という疑問への答えは、明確な「ノー」です。マルウェアのカテゴリーには、ウイルスのほかに、スパイウェア、アドウェア、ランサムウェアなど、さまざまな有