MENU

JavaScript関数・メソッド構文変数

【JavaScript】 関数の定義と変数代入(関数式)の違い

更新日:2021/08/23

 

僕がJavaScriptと学び始めたとき、関数の定義について次のように感じました。

 

function a(){};
とか
const a = function (){};
とか、いくつも関数定義の方法用意しとくな!
わかりにくい!!

 

 

サンプルプログラムによって関数定義の方法が異なっていて、読み解くのに苦労したものです。

 

しかし実は、この二つの書き方は目的が異なることを最近になって知りました。
今回はJavaScriptの関数定義についてまとめてみます。

 

関数宣言と関数式

 

関数を定義するには、関数宣言と関数式の二つの方法があります。
この二つの違いを説明する前に、そもそも「JavaSceiptの関数とは何か?」という点についてお伝えします。

 

JavaScriptの関数とは

 

JavaSceiptの関数は、functionオブジェクト(関数オブジェクト)というオブジェクトの一種です。
functionオブジェクトは名前の後に( )をつけることで、関数コードを実行できるという特徴があります。

 

しかし基本的には自分で作成したオブジェクトと同じものです。
変数に代入したり、引数として渡すことができるのです。

 

では、関数宣言と関数式についてお伝えします。

 

関数宣言

 

functionから始まる文が関数宣言です。

 

関数宣言はブロック( { } )内で使用できるfunctionオブジェクトを作成して、名前をつけます。

 

次のコードは、messageという引数を受け付けるkansuという名前のfunctionオブジェクトを作成しています。

 

関数宣言

 


function kansu( message ) {
  alert( message );
}
kansu( "test !" );

 

 

実行すると、アラートで「 test! 」と表示されます。

 

ブラウザ アラート表示

 

ただし、関数宣言そのものは実体を生成しません。
(ブロック内の最初のコード実行前に、生成されます)
即時関数のような使い方はできません。

 

関数宣言をそのまま実行

 


function f(){
    let a = 0;
}();

 

上のコードは、次のようなエラーが発生します。

 

SyntaxError: expected expression, got ')'

 

 

関数式とは

 

次のコードは関数式です。

 

関数式

 


const kansu = function( message ){
    alert( message );
};
kansu( "test !" );

 

実行すると先ほどと同じように、アラートで「 test! 」と表示されます。

 

このコードは、変数kansuに、"function(message){・・・}" という文字列を入れているのではありません。

 

具体的には function(message){・・・} で、名前のないfunctionオブジェクトが作成されます。
次に作成されたオブジェクトが、変数kansuに代入されます。

 

イコールで代入するだけでなく、次のようなコールバックに指定した関数も、関数式です。

 

関数式

 


setTimeout ( function( ){
    alert( "3秒経過" );
},3000);

 

function・・・ で作成されたfunctionオブジェクトが、引数としてsetTimeoutに渡されています。

 

関数宣言かどうかは、文がfunctionで始まるかどうかによって決まります。

 

文がfunctionで始まるなら、関数宣言です。
functionで始まらないなら、関数式です。

 

次の文は ( で始まっているので、関数式になります。

 

関数式

 

(function kansu(message){
    alert(message);
});
kansu("test");//ReferenceError:kansu is not defined

 

この関数式は、function・・・で関数オブジェクトを作成します。
しかし関数宣言ではないため、kansuという名前が割り当てられません。

 

そのため、kansuという名前の関数が存在しないので、"kansuが未定義です" という意味のエラーが表示されます。

 

(から始まるコードは、あまり役に立たないように見えますが、即時関数で使用することが多いです。

 

即時関数

 


(function (message){
    alert(message);
})("test");

 

作成されたfunctionオブジェクトを即座に( )で実行しています。
上のコード程度では、「alert("test")でいいのでは?」という疑問が出てきますが、テクニックとして知っておいてください。

 

即時関数については、次の記事を確認してみてください。
【JavaScript】 即時関数の挙動について調べてみた

 

 

関数宣言と関数式の違い

 

関数宣言と関数式はどんな点が異なるのでしょうか。

 

関数宣言は後ろで記述されていてもOK

 

関数宣言されている関数は、宣言前でも実行できます。
JavaScriptの仕様では、コード評価時に関数宣言がブロックの一番上に移動します。
この動作を巻き上げ(ホイスティング)といいます。

 

JavaScript:関数宣言の巻き上げ

 


function kansuB(a){    kansuA(a); };    // 実行時に巻き上げで移動(しているように見える)
function kansuA(a){  console.log(a);};  // 実行時に巻き上げで移動(しているように見える)

kansuA("a");  // 宣言が後ろでも実行できる
kansuB("b");  // 宣言が後ろでも実行できる

function kansuB(a) { // コード上の位置 ↑実行時上へ移動
    kansuA(a);
}

function kansuA(a){ // コード上の位置 ↑実行時上へ移動
    console.log(a);
}

 

関数式は後ろで記述されているとNG

 

関数式も、関数宣言と同様に巻き上げがおこります。

 

ですが実際には、エラーが表示されます。

 

JavaScript:関数式の巻き上げ

 


kansuA("a");//ReferenceError: can't access lexical declaration `kansuA' before initialization
kansuB("b");//ReferenceError: can't access lexical declaration `kansuB' before initialization

const kansuB = function(a){ // コード上の位置
    kansuA(a);
};

const kansuA =function(a){ // コード上の位置
    console.log(a);
};

 

巻き上げで、次のような変数の移動がおこっています。
しかし移動されただけで、変数に値がなにも入っていません。
何もセットされていない変数を参照したために、エラーが表示されたようです。

 

JavaScript:関数式の巻き上げ

 


const kansuB; // 実行時に巻き上げで変数宣言のみ移動 値未設定
const kansuA; // 実行時に巻き上げで変数宣言のみ移動 値未設定

kansuA("a");  // ReferenceError: can't access lexical declaration `kansuA' before initialization
kansuB("b");  // ReferenceError: can't access lexical declaration `kansuB' before initialization

const kansuB = function(a){ // コード上の位置
    kansuA(a);
}

const kansuA = function(a){ // コード上の位置
    console.log(a);
};

 

ちなみにconstではなくてvarを使用した場合、値としてundefindがセットされます。
そのためコンソールに「undefind」と表示されます。

 

いずれにしても、関数式は定義前に使用するべきではありません。

 

ただし、関数内で使用することは可能です。

 

上のコードでは、kansuB内でkansuAを実行しています。

 

この書き方については問題ありません。

 

問題はkansuBをどこで実行するかです。

 

JavaScript:関数式の巻き上げ

 


const kansuB = function(a){ // コード上の位置
    kansuA(a);
}

kansuB( "a" );  // ReferenceError: can't access lexical declaration `kansuA' before initialization

const kansuA = function(a){ // コード上の位置
    console.log(a);
};

kansuB( "b" ); // b

 

上のコードでは、最初のkansuB呼び出しでエラーになります。
理由は、 kansuAがまだ未定義のためです。

 

2番目のkansuB呼び出し時点では、 kansuAに関数オブジェクトがセットされているため、エラーになりません。

 

巻き上げについては、次の記事を読んでみてください。
【JavaScript】 変数・関数の巻き上げってなんだ?

関数宣言の注意点:上書きができる

 

同名の関数宣言は上書きされます。
次のコードは、エラーになりません。

 

JavaScript

 


function a(){
    console.log("1");
}
function a(){
    console.log("2");
}

a();  // 結果: 2

 

 

 

 

複数の外部スクリプトを読み込んでいると、同名の関数が定義されている可能性が大きくなります。

 

デバッグが難しくなるので、即時関数などを使って、グローバル汚染しないように注意する必要があります。
■参考:【JavaScript】 即時関数の挙動について調べてみた

 

関数式の注意点:作成時の変数が維持される

 

関数オブジェクトは、作成時に同スコープの変数への参照を保持します。

 

JavaScript


(function(){
    let a = 0; // ・・・ (1)

    const kansuA = function(){
        console.log(a); // どこでkansuAを実行しても、このaは(1)
    };

    function kansuB(callBack){
       let a = 100;
       callBack(); // kansuAが実行される
    }

    kansuA();              // 定義と同じ階層で実行 → 結果 : 0
    kansuB(kansuA);   // kansuB内でkansuAを実行→結果 : 0→kansuB内のaはkansuAで参照されない!
    
})();

 

上の例では、kansuA作成時に外側の「let a = 0;」への参照を関数オブジェクト(kansuA)内に作成します。

 

kansuBにkansuAを渡していますがkansuAの実行では、「let a = 100;」は参照されません。
kansuAが保持している変数aへの参照が使用されます。

 

JavaScriptはレキシカル環境というものが採用されており、関数が定義された時の状態を維持し続けるのです。

 

関数宣言も同じことが言えますが、コールバック関数として無名関数を使用するときなど、特に注意が必要な知識です。

 

これはクロージャーという概念を元にしたものです。
詳しくは次の記事を読んでみてください。
【JavaScript】 クロージャとは?要点をまとめてみる

けーちゃんおススメJavaScript入門書

  • スラスラ読める JavaScript ふりがなプログラミング
  • プログラム未経験者がJavaScript始めるならコレ!
    コードを掲載して自分で理解しろという投げっぱなしな入門書とは異なり、コードに一つ一つどんなことをやっているかをふりがなという形式で解説しています。
    それでいてJavaScriptの基礎と応用を学べる良書です。
  • これからWebをはじめる人のHTML&CSS、JavaScriptのきほんのきほん
  • JavaScriptの機能を実践で活かすにはHTMLやCSSの知識が不可欠です。
    しかしそれらの知識があることが前提として書かれている書籍が多い中、この本は総合的な知識を身に着けることができます。
    HTMLやCSSの知識も不安な方には、ぴったりの一冊です
  •  

    入門書の役割は、自分のやりたいことをネットで調べることができるようになるための、基礎的な知識の獲得です。
    まずはこれらの本でしっかりと基礎知識を身につけましょう。
    そしてもっと高度なことや専門的なことはネットで調べ、情報が足りないと感じたら書籍を購入してください。


    記事の内容について

     

    こんにちはけーちゃんです。
    説明するのって難しいですね。


    「なんか言ってることおかしくない?」
    たぶん、こんなご意見あると思います。

    裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

    そんなときは、ご意見もらえたら嬉しいです。

    ご意見はこちら。
    https://note.affi-sapo-sv.com/info.php

    【お願い】

    お願い

    ■このページのURL


    ■このページのタイトル


    ■リンクタグ


    ※リンクして頂いた方でご希望者には貴サイトの紹介記事を作成してリンクを設置します。
    サイト上部の問い合わせよりご連絡ください。
    ただしサイトのジャンルによっては、お断りさせていただくことがあります。