はじめに

プログラミングにおいて、「関数」は最も重要な概念の一つです。関数を使うことで、繰り返し使用するコードをまとめ、プログラムを整理し、保守しやすくすることができます。

JavaScriptでは、関数を定義する方法として「関数宣言」「関数式」「アロー関数」の3つがあります。それぞれに特徴があり、状況に応じて使い分けることが重要です。

本記事では、以下の内容を初心者向けにわかりやすく解説します。

  • 関数とは何か、なぜ使うのか
  • 関数宣言・関数式・アロー関数の書き方と違い
  • 引数と戻り値の基本的な使い方
  • デフォルト引数と残余引数
  • 関数の巻き上げ(Hoisting)の仕組み
  • 3つの関数定義方法の使い分け指針

関数とは

関数の役割

関数とは、特定の処理をまとめて名前を付けたものです。関数を使うことで、同じ処理を何度も書く必要がなくなり、コードの再利用性が高まります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 関数を使わない場合:同じ処理を何度も書く必要がある
console.log("こんにちは、田中さん!");
console.log("こんにちは、佐藤さん!");
console.log("こんにちは、鈴木さん!");

// 関数を使う場合:処理をまとめて再利用できる
function greet(name) {
  console.log(`こんにちは、${name}さん!`);
}

greet("田中"); // こんにちは、田中さん!
greet("佐藤"); // こんにちは、佐藤さん!
greet("鈴木"); // こんにちは、鈴木さん!

関数を使うメリット

関数を使うことで、以下のメリットが得られます。

メリット 説明
コードの再利用 同じ処理を何度も書く必要がなくなる
可読性の向上 処理に名前を付けることで、コードの意図が明確になる
保守性の向上 修正が必要な場合、関数の中身だけを変更すればよい
テストの容易さ 関数単位でテストを書くことができる

関数宣言

基本構文

関数宣言は、functionキーワードを使って関数を定義する最も基本的な方法です。

1
2
3
4
function 関数名(引数1, 引数2, ...) {
  // 処理
  return 戻り値;
}

具体例

2つの数値を加算する関数の例を見てみましょう。

1
2
3
4
5
6
7
8
// 関数宣言
function add(a, b) {
  return a + b;
}

// 関数の呼び出し
const result = add(3, 5);
console.log(result); // 8

各部分の役割は以下のとおりです。

部分 説明
function 関数を定義するためのキーワード
add 関数名(任意の名前を付けられる)
(a, b) 引数リスト(関数に渡す値を受け取る変数)
{ ... } 関数本体(実行される処理)
return 戻り値を返すキーワード

関数宣言の特徴

関数宣言には「巻き上げ(Hoisting)」という特徴があります。これは、関数宣言がスコープの先頭に巻き上げられるため、宣言より前に関数を呼び出せるという仕組みです。

1
2
3
4
5
6
// 関数宣言より前に呼び出しても動作する
console.log(square(5)); // 25

function square(n) {
  return n * n;
}

この動作は便利ですが、コードの可読性を考えると、関数は使用する前に定義しておくことをおすすめします。

関数式

基本構文

関数式は、関数を変数に代入する形で定義する方法です。

1
2
3
4
const 変数名 = function(引数1, 引数2, ...) {
  // 処理
  return 戻り値;
};

具体例

1
2
3
4
5
6
7
8
// 関数式
const multiply = function(a, b) {
  return a * b;
};

// 関数の呼び出し
const result = multiply(4, 6);
console.log(result); // 24

名前付き関数式

関数式には名前を付けることもできます。名前を付けると、再帰呼び出しやデバッグ時のスタックトレースで役立ちます。

1
2
3
4
5
6
7
8
9
// 名前付き関数式(階乗を計算する例)
const factorial = function fact(n) {
  if (n <= 1) {
    return 1;
  }
  return n * fact(n - 1); // 関数名 fact で自分自身を呼び出す
};

console.log(factorial(5)); // 120 (5 * 4 * 3 * 2 * 1)

関数式の特徴

関数式は関数宣言と異なり、巻き上げが発生しません。そのため、定義より前に呼び出すとエラーになります。

1
2
3
4
5
6
// エラー: Cannot access 'subtract' before initialization
console.log(subtract(10, 3));

const subtract = function(a, b) {
  return a - b;
};

アロー関数

基本構文

アロー関数は、ES6(ECMAScript 2015)で導入された新しい関数の書き方です。=>(アロー)を使って、より短く関数を定義できます。

1
2
3
4
const 変数名 = (引数1, 引数2, ...) => {
  // 処理
  return 戻り値;
};

具体例

1
2
3
4
5
6
// アロー関数
const divide = (a, b) => {
  return a / b;
};

console.log(divide(20, 4)); // 5

省略記法

アロー関数には、さらに短く書ける省略記法があります。

引数が1つの場合

引数が1つだけの場合、括弧を省略できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 括弧あり
const double = (n) => {
  return n * 2;
};

// 括弧なし(引数が1つの場合)
const triple = n => {
  return n * 3;
};

console.log(double(5)); // 10
console.log(triple(5)); // 15

処理が1行の場合

処理が1行で、その結果を返す場合は、波括弧とreturnを省略できます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 通常の書き方
const square = (n) => {
  return n * n;
};

// 省略記法(暗黙のreturn)
const cube = n => n * n * n;

console.log(square(4)); // 16
console.log(cube(4));   // 64

引数がない場合

引数がない場合は、空の括弧()が必要です。

1
2
3
const greet = () => "こんにちは!";

console.log(greet()); // こんにちは!

配列メソッドとの組み合わせ

アロー関数は、配列のメソッド(mapfilterreduceなど)と組み合わせて使うと、非常に簡潔にコードを書けます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const numbers = [1, 2, 3, 4, 5];

// 各要素を2倍にする
const doubled = numbers.map(n => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]

// 偶数のみを抽出する
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // [2, 4]

// 合計を計算する
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(sum); // 15

引数の基本

引数とは

引数は、関数に渡す値のことです。関数を呼び出すときに引数を渡すことで、同じ関数でも異なるデータに対して処理を実行できます。

1
2
3
4
5
6
7
function greet(name, time) {
  console.log(`${time}${name}さん!`);
}

greet("田中", "おはよう");   // おはよう、田中さん!
greet("佐藤", "こんにちは"); // こんにちは、佐藤さん!
greet("鈴木", "こんばんは"); // こんばんは、鈴木さん!

引数が足りない場合

関数に渡す引数が足りない場合、不足している引数はundefinedになります。

1
2
3
4
5
6
function showInfo(name, age) {
  console.log(`名前: ${name}, 年齢: ${age}`);
}

showInfo("田中");        // 名前: 田中, 年齢: undefined
showInfo("佐藤", 25);    // 名前: 佐藤, 年齢: 25

デフォルト引数

ES6以降では、引数にデフォルト値を設定できます。引数が渡されなかった場合(またはundefinedが渡された場合)、デフォルト値が使用されます。

1
2
3
4
5
6
function greet(name, greeting = "こんにちは") {
  console.log(`${greeting}${name}さん!`);
}

greet("田中");           // こんにちは、田中さん!
greet("佐藤", "おはよう"); // おはよう、佐藤さん!

デフォルト引数は、アロー関数でも同様に使用できます。

1
2
3
4
5
6
const createUser = (name, role = "guest") => {
  return { name, role };
};

console.log(createUser("田中"));         // { name: "田中", role: "guest" }
console.log(createUser("佐藤", "admin")); // { name: "佐藤", role: "admin" }

残余引数

残余引数(rest parameters)を使うと、不特定多数の引数を配列として受け取ることができます。...(スプレッド構文)を引数名の前に付けて使用します。

1
2
3
4
5
6
function sum(...numbers) {
  return numbers.reduce((acc, n) => acc + n, 0);
}

console.log(sum(1, 2));          // 3
console.log(sum(1, 2, 3, 4, 5)); // 15

残余引数は、他の引数と組み合わせて使うこともできます。ただし、残余引数は最後の引数でなければなりません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function greetAll(greeting, ...names) {
  names.forEach(name => {
    console.log(`${greeting}${name}さん!`);
  });
}

greetAll("こんにちは", "田中", "佐藤", "鈴木");
// こんにちは、田中さん!
// こんにちは、佐藤さん!
// こんにちは、鈴木さん!

戻り値の基本

戻り値とは

戻り値は、関数が処理を終えた後に返す値のことです。return文を使って戻り値を指定します。

1
2
3
4
5
6
function add(a, b) {
  return a + b; // a + b の結果を戻り値として返す
}

const result = add(3, 5); // 戻り値を変数に格納
console.log(result);      // 8

return文の動作

return文が実行されると、関数の処理はそこで終了します。return以降のコードは実行されません。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
function checkAge(age) {
  if (age < 0) {
    return "年齢は0以上である必要があります";
  }
  if (age < 20) {
    return "未成年です";
  }
  return "成人です";
  
  console.log("この行は実行されません"); // 到達不能コード
}

console.log(checkAge(15)); // 未成年です
console.log(checkAge(25)); // 成人です

戻り値がない場合

return文がない関数、または値を指定しないreturn文を持つ関数は、undefinedを返します。

1
2
3
4
5
6
7
function sayHello(name) {
  console.log(`こんにちは、${name}さん!`);
  // return がないので undefined を返す
}

const result = sayHello("田中");
console.log(result); // undefined

オブジェクトを返す場合の注意

アロー関数でオブジェクトを返す場合、波括弧がブロックと解釈されないよう、括弧で囲む必要があります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
// エラー: 波括弧がブロックとして解釈される
// const createUser = name => { name: name };

// 正しい書き方: 括弧で囲む
const createUser = name => ({ name: name });

// ES6のプロパティ短縮記法も使用可能
const createUserShort = name => ({ name });

console.log(createUser("田中"));      // { name: "田中" }
console.log(createUserShort("佐藤")); // { name: "佐藤" }

3つの関数定義方法の比較

比較表

関数宣言、関数式、アロー関数の違いを表にまとめます。

特徴 関数宣言 関数式 アロー関数
構文 function name() {} const name = function() {} const name = () => {}
巻き上げ あり(宣言前に呼び出し可能) なし なし
名前 必須 任意(無名関数も可) なし(無名関数のみ)
thisの束縛 呼び出し時に決定 呼び出し時に決定 定義時のスコープを継承
argumentsオブジェクト あり あり なし
コンストラクタとして使用 可能 可能 不可

使い分けの指針

それぞれの関数定義方法には適した使用場面があります。

flowchart TD
    A[関数を定義する] --> B{巻き上げが必要?}
    B -->|はい| C[関数宣言を使用]
    B -->|いいえ| D{コールバック関数?}
    D -->|はい| E{thisを使う?}
    E -->|はい| F[関数式を使用]
    E -->|いいえ| G[アロー関数を使用]
    D -->|いいえ| H{短い処理?}
    H -->|はい| G
    H -->|いいえ| I[関数式または関数宣言を使用]

関数宣言を使う場面

  • プログラムのメインとなる関数
  • ファイル内のどこからでも呼び出したい関数
  • 複雑なロジックを持つ大きな関数
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// メインの処理を行う関数
function main() {
  const data = fetchData();
  processData(data);
  displayResult(data);
}

function fetchData() {
  // データ取得処理
}

function processData(data) {
  // データ処理
}

function displayResult(data) {
  // 結果表示
}

関数式を使う場面

  • 条件に応じて関数を代入したい場合
  • 関数を変数として扱いたい場合
  • 名前付き関数式で再帰を行いたい場合
1
2
3
4
5
6
7
// 条件に応じて関数を選択
let calculate;
if (mode === "add") {
  calculate = function(a, b) { return a + b; };
} else {
  calculate = function(a, b) { return a - b; };
}

アロー関数を使う場面

  • 配列メソッドのコールバック関数
  • 短い処理を簡潔に書きたい場合
  • thisを外側のスコープから継承したい場合
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 配列メソッドとの組み合わせ
const users = [
  { name: "田中", age: 25 },
  { name: "佐藤", age: 30 },
  { name: "鈴木", age: 20 }
];

// 名前だけを抽出
const names = users.map(user => user.name);
console.log(names); // ["田中", "佐藤", "鈴木"]

// 25歳以上のユーザーを抽出
const adults = users.filter(user => user.age >= 25);
console.log(adults); // [{ name: "田中", age: 25 }, { name: "佐藤", age: 30 }]

よくある間違いと注意点

アロー関数とthis

アロー関数は独自のthisを持たず、定義時のスコープのthisを継承します。これはオブジェクトのメソッドとして使う場合に問題になることがあります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
const counter = {
  count: 0,
  
  // 通常の関数: thisはcounterオブジェクトを参照
  increment: function() {
    this.count++;
    console.log(this.count);
  },
  
  // アロー関数: thisはcounterオブジェクトを参照しない
  decrement: () => {
    this.count--; // thisはグローバルオブジェクトを参照
    console.log(this.count);
  }
};

counter.increment(); // 1
counter.decrement(); // NaN(thisがcounterを参照していない)

オブジェクトのメソッドには、通常の関数またはメソッド短縮記法を使用しましょう。

1
2
3
4
5
6
7
8
9
const counter = {
  count: 0,
  
  // メソッド短縮記法(推奨)
  increment() {
    this.count++;
    console.log(this.count);
  }
};

暗黙のreturnでの改行

アロー関数の暗黙のreturnで、処理の前に改行を入れると意図しない動作になることがあります。

1
2
3
4
5
6
7
8
// 意図しない動作: undefinedを返す
const getObject = () =>
  { name: "田中" }; // ブロックとして解釈される

// 正しい書き方
const getObjectCorrect = () => (
  { name: "田中" }
);

まとめ

本記事では、JavaScriptの関数について、基本から実践的な使い方までを解説しました。

  • 関数宣言は最も基本的な書き方で、巻き上げによって定義前に呼び出すことができます
  • 関数式は関数を変数に代入する形で定義し、条件に応じて関数を選択する場合などに便利です
  • アロー関数は簡潔な構文で、配列メソッドのコールバックなど短い処理に適しています
  • 引数にはデフォルト値や残余引数を設定でき、柔軟な関数設計が可能です
  • 戻り値return文で指定し、関数の処理結果を呼び出し元に返します

関数はJavaScriptプログラミングの根幹をなす概念です。3つの書き方の違いを理解し、状況に応じて適切に使い分けられるようになることで、より読みやすく保守しやすいコードが書けるようになります。

次のステップとして、スコープとクロージャ、コールバック関数と高階関数について学ぶと、関数をより深く理解できるようになります。

参考リンク