JavaScript の関数には「関数宣言」「関数式」という2つの作り方があります。
ただ見た目が違うだけに見えるのですが、実は仕様の違いもあるのでまとめてみます。
関数宣言の書き方
function
キーワードを使って関数を宣言できます。
function calcRectArea(width, height) {
return width * height;
}
console.log(calcRectArea(5, 6));
// expected output: 30
// 引用: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/function
The 関数って感じですね。だって頭に function
ってついてるし。
関数宣言しますよ、ということを JavaScript に伝える function
キーワードの後に関数名を記述し、丸括弧の中に仮引数、波括弧の中(関数のボディ)に関数の処理を記述します。
function 関数名(仮引数0個〜いくつでも) {
関数の処理
}
関数式の書き方
const calcRectArea = function(width, height) {
return width * height;
};
console.log(calcRectArea(5, 6));
// expected output: 30
関数宣言を関数式に書き直してみました。どこが変わったでしょうか?
// 関数宣言
function calcRectArea(width, height) {
return width * height;
}
// 関数式
const calcRectArea = function(width, height) {
return width * height;
};
まず、calcRectArea
という変数の中に関数が格納されていますね。JavaScript の関数は 第一級オブジェクト なので変数に代入することができます。
そして関数名がありません。名前は変数名で表現できているので関数自体につける必要がないんですね👀
名前のない関数を 無名関数 と言います。
また関数式は以下のような形で書くこともできます。
const calcRectArea = (width, height) => {
return width * height;
};
console.log(calcRectArea(5, 6));
// expected output: 30
ES6 で導入された アロー関数 です。
ちょっと変わった書き方のように見えますが、 function
キーワードが消滅した代わりにアロー、矢印( =>
)が追加されただけです。
ちなみにアローはファットアローとも言います。
function
というとっても長いキーワードを書かなくて済むので便利ですが、アロー関数が他の関数と全く同じ機能を持っているわけではなく、MDN によると以下の違いがあるそうです。
- アロー関数には、this、arguments、super への結びつけがないので、メソッドとして使用することはできません。
- アロー関数には new.target キーワードがありません。
- アロー関数は、call、apply、bind のような、一般にスコープを確立することを前提としたメソッドには適していません。
- アロー関数はコンストラクターとして使用することはできません。
- アロー関数は本体内で yield を使用することはできません。
アロー関数内で this
を使うのは避けましょう。これはアドベントカレンダーに this
についての記事を作成予定なので、作成しだい追記します。
関数宣言と関数式の違い
ここまで関数宣言と関数式の記法を見ていきました。
- 関数宣言:
function
キーワードを使って宣言 - 関数式:
function
キーワードを使って、変数に代入する形で無名関数を宣言 / アロー関数を変数に代入
結局のところ違うのは書き方だけ? かというと、そういうこともありません。
関数宣言では 巻き上げ が起こります。
上記のコード断片はコードが動作するように書いたよう期待する方法です。今度は、関数を書く前に関数を呼び出したらどうでしょう。
catName("Chloe");
function catName(name) {
console.log("My cat's name is " + name);
}
/*
上記のコードの結果は: "My cat's name is Chloe"
*/
コード内で関数を書く前に、関数呼び出しを最初に書いても、コードは動作します。これは JavaScript でコンテキスト実行が動作するためです。
Hoisting (巻き上げ、ホイスティング) - MDN Web Docs 用語集: ウェブ関連用語の定義 | MDN
つまり関数を宣言する前の位置であっても関数を使用することができます。
関数式ではこの動作はありません。
catName("Chloe");
const catName = function(name) {
console.log("My cat's name is " + name);
}
// Uncaught ReferenceError: catName is not defined
// アロー関数でもできません
catName("Chloe");
const catName = (name) => {
console.log("My cat's name is " + name);
}
// Uncaught ReferenceError: catName is not defined
ちなみに巻き上げ可能だからこっちの方が絶対便利だよ! というわけでもなく、これが仕様です。
番外編: Function コンストラクターとは
Function コンストラクターは、新しい Function オブジェクトを生成します。コンストラクターを直接呼び出すと動的に関数を生成することができますが、セキュリティや、 eval (en-US) と似た性能の (ただし、はるかに重要性の低い) 問題を抱えます。ただし eval とは異なり、 Function コンストラクターはグローバルスコープで実行される関数のみを生成します。
通常は関数を作る目的で使用されることはおそらくないであろう、 Function コンストラクターという方法もあります。
▼ コンストラクターについてはこちらの記事で触れています
僕は一度だけ、ユーザーの入力した情報をもとに関数を作成するのに Function コンストラクターを使用しました。
const sum = new Function('a', 'b', 'return a + b');
console.log(sum(2, 6));
// expected output: 8
// MDN より: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/Function
まとめ
- 関数式では無名関数 / アロー関数が使える
- 関数宣言では巻き上げが起こる
さて、本日は執筆に協力してくれた共著者がいるのでご紹介しておきましょう。 ChatGPT 君です。
末恐ろしい子ですね〜。AI の進化めちゃくちゃ楽しみです