Michi's Tech Blog

一人前のWebエンジニアを目指して

JavaScriptの関数を完全に理解する①

こんにちは!
スマレジ テックファームのMichiです!

今回は「JavaScriptの関数を完全に理解する」というテーマで、JSの関数について整理したいと思います。

function命令で定義する

基本形

function getTriangle(base, height) {
  return base * height / 2
}

/* 呼び出し */
getTriangle(5, 2)  // 結果:5

まずは基本形から。
function命令を用いたシンプルな記述ですが、実務ではほぼ見かけない気がします。

無名関数(匿名関数)

function(base, height) {
  return base * height / 2
}

先ほどの形から、function後の関数名を省略したタイプです。
その場限りで一度しか使用されない関数に使用されます。これも実務ではほぼ見かけない印象。

関数リテラル表現で定義する

リテラルとは、データ型に格納できる値そのもの、また、値の表現方法のことです。ま、簡単に言えば「変数に格納できる値」と考えてもらって大丈夫です。 JavaScriptにおいては関数もデータ型の一種です。なので、関数もリテラルとして表現できるし、関数リテラルを変数に代入することもできます。

const getTriangle = function(base, height) {
  return base * height / 2
}

/* 呼び出し */
getTriangle(5, 2)  // 結果:5

以上を踏まえて、関数リテラルを使って表現したのがこの形。先ほどの無名関数を変数に代入しています。

アロー関数で定義する

基本形

const getTriangle = (base, height) => {
  return base * height / 2
}

アロー関数を利用することで、先ほどの関数リテラルをよりシンプルに記述できます。
アロー関数ではfunctionの代わりに、=>(アロー)で引数と関数本体をつなぎます。だんだんと実務でよく見る形になってきました。

returnと{}の省略

const getTriangle = (base, height) => base * height / 2

関数本体({...}の中身)が1文である場合には、{}を省略できます。 また、文の値がそのまま戻り値とみなされるので、return命令も省略可能です

引数の()の省略

const getCircle = radius => radius * radius * Math.PI

さらに、引数が1個の場合は、引数をくくる()も省略できます。
さすがにここまで省略すると分かりにくいので、うちのチームではこの形は使わないルールになっています。

※注意

const hello = () => console.log('Hello World!')

上記のように、引数がない場合は、()を省略することはできません。

function命令と関数リテラルの違い

ここまではちょっとJSを触ったことがある人なら知ってること。
ここで私思いました。「結局、function命令と関数リテラルの違いって何よ?」って。
ということで、詳しく調べてみました。

関数リテラルは実行時に評価される

console.log('三角形の面積:' + getTriangle(5, 2))

const getTriangle = function(base, height) {
  return base * height / 2
}

// 結果:Uncaught ReferenceError: Cannot access 'getTriangle' before initialization 

上のコードはエラーになります。
console.logで呼び出している時点で、変数getTriangleはまだ定義されていないので当然です。 関数リテラルで定義する場合は、呼び出し元のコード(console.log)より先に記述する必要があるのです。
この結果から、「関数リテラルは実行時(代入時)に評価される」ということが分かります。

function命令は静的な構造を宣言する

console.log('三角形の面積:' + getTriangle(5, 2))

function getTriangle(base, height) {
  return base * height / 2
}

// 結果:"三角形の面積:5"

では、function命令で記述したときはどうでしょうか?
普通に考えれば、console.logで呼び出している時点で、関数getTriangleはまだ定義されていないので、これもエラーになるような気がしますが、実際には問題なく動作します。
これはfunctionが動的に実行される命令ではなく、静的な構造を宣言するためのキーワードだからです。「静的な構造」というと分かりにくいですが、要するに、「function命令はコードを解析/コンパイルするタイミングで関数を登録している」 ということです。 したがって、実行時にはすでに関数getTriangleは定義されているため、ファイル内のどこからでも呼び出すことができるというわけですね。

まとめ

JavaScriptの基本的な関数構文、およびそれぞれの違いについて説明しました。
続きは「JavaScriptの関数を完全に理解する②」でやります。