JavaScriptのコールバック関数:関数を引数として渡す

配列メソッド(forEach、filter、mapなど)を学んだ際に出てきた「関数を引数に渡す」という概念。これが「コールバック関数」です。

コールバック関数とは

コールバック関数は、引数に入っている関数のことです。

const numbers = [1, 2, 3];

numbers.forEach((num) => {
  console.log(num);
});

この (num) => { console.log(num); } の部分が、コールバック関数です。

コールバック関数の仕組み

普通の関数を引数として渡す

コールバック関数は、まず普通の関数として定義しておき、それを引数として渡すこともできます。

// 関数を定義
const printNum = (num) => {
  console.log(num);
};

const numbers = [1, 2, 3];

// 関数を引数として渡す
numbers.forEach(printNum);
// 1
// 2
// 3

forEach は、配列の各要素に対して printNum 関数を呼び出しています。

コールバック関数を呼び出すときは()をつける

関数を実行するときは、() をつけます。

const greet = () => {
  console.log("こんにちは");
};

// 関数を呼び出す(実行する)
greet();  // こんにちは

コールバック関数を渡すときは()をつけない

関数を引数として渡すときは、()つけません

const greet = () => {
  console.log("こんにちは");
};

const numbers = [1, 2, 3];

// ❌ ()をつけると、関数が実行されてしまう
numbers.forEach(greet());  // エラー

// ✅ ()をつけないと、関数自体を渡せる
numbers.forEach(greet);

重要: greet() は「関数を実行する」、greet は「関数自体を渡す」という違いがあります。

例:タイマー関数

const sayHello = () => {
  console.log("こんにちは");
};

// ❌ ()をつけると即座に実行される
setTimeout(sayHello(), 1000);  // すぐに「こんにちは」と表示される

// ✅ ()をつけないと1秒後に実行される
setTimeout(sayHello, 1000);  // 1秒後に「こんにちは」と表示される

関数を直接引数の中で定義する

コールバック関数は、引数の中で直接定義することもできます。これが最も一般的な書き方です。

const numbers = [1, 2, 3];

// 関数を直接定義
numbers.forEach((num) => {
  console.log(num * 2);
});
// 2
// 4
// 6

わざわざ関数名をつけて定義する必要がなく、コードがシンプルになります。

コールバック関数に引数を渡す

コールバック関数にも、普通の関数と同じように引数を渡すことができます

const numbers = [1, 2, 3, 4, 5];

// コールバック関数の引数numに、配列の各要素が渡される
numbers.forEach((num) => {
  console.log(`数値: ${num}`);
});
// 数値: 1
// 数値: 2
// 数値: 3
// 数値: 4
// 数値: 5

forEach は、配列の各要素を順番に num に代入してコールバック関数を実行します。

引数の数を合わせる

コールバック関数を定義するとき、引数の数を合わせる必要があります

forEachの場合

forEach のコールバック関数は、最大3つの引数を受け取れます。

const fruits = ["りんご", "バナナ", "オレンジ"];

fruits.forEach((fruit, index, array) => {
  console.log(`${index}: ${fruit}`);
  console.log(`配列の長さ: ${array.length}`);
});
// 0: りんご
// 配列の長さ: 3
// 1: バナナ
// 配列の長さ: 3
// 2: オレンジ
// 配列の長さ: 3

引数の意味

  1. 第1引数:要素そのもの
  2. 第2引数:インデックス番号
  3. 第3引数:配列全体

必要な引数だけ使えばOKです。

// 要素だけ使う
fruits.forEach((fruit) => {
  console.log(fruit);
});

// 要素とインデックスを使う
fruits.forEach((fruit, index) => {
  console.log(`${index}: ${fruit}`);
});

自作関数でコールバック関数を使う

自分で作った関数でも、コールバック関数を引数として受け取ることができます。

const repeat = (count, callback) => {
  for (let i = 0; i < count; i++) {
    callback(i);  // コールバック関数を呼び出す
  }
};

// 使い方
repeat(3, (num) => {
  console.log(`${num}回目`);
});
// 0回目
// 1回目
// 2回目

callback(i) で、渡されたコールバック関数を実行しています。

実用例:処理の共通化

const processNumbers = (numbers, callback) => {
  const results = [];
  numbers.forEach((num) => {
    results.push(callback(num));
  });
  return results;
};

const numbers = [1, 2, 3];

// 2倍にする
const doubled = processNumbers(numbers, (num) => num * 2);
console.log(doubled);  // [2, 4, 6]

// 3倍にする
const tripled = processNumbers(numbers, (num) => num * 3);
console.log(tripled);  // [3, 6, 9]

コールバック関数を使うことで、同じ処理の「やり方」だけを変えられます。

Pythonとの比較

Pythonにも似た概念があります。

# Python
def repeat(count, callback):
    for i in range(count):
        callback(i)

repeat(3, lambda i: print(f"{i}回目"))
// JavaScript
const repeat = (count, callback) => {
  for (let i = 0; i < count; i++) {
    callback(i);
  }
};

repeat(3, (i) => {
  console.log(`${i}回目`);
});

Pythonの lambda は、JavaScriptのアロー関数に相当します。

コールバック関数を使うメリット

1. コードの再利用性が高まる

const calculate = (a, b, operation) => {
  return operation(a, b);
};

console.log(calculate(5, 3, (x, y) => x + y));  // 8(足し算)
console.log(calculate(5, 3, (x, y) => x * y));  // 15(掛け算)

2. 処理を柔軟に変えられる

const numbers = [1, 2, 3, 4, 5];

// 偶数だけ抽出
const evens = numbers.filter((num) => num % 2 === 0);

// 3より大きい数だけ抽出
const greaterThan3 = numbers.filter((num) => num > 3);

同じ filter メソッドでも、コールバック関数を変えることで異なる結果が得られます。

今日の学びのポイント

  • コールバック関数: 引数に入っている関数
  • 関数を呼び出すとき() をつける
  • 関数を渡すとき() をつけない
  • 関数を直接引数の中で定義することもできる
  • コールバック関数にも引数を渡せる
  • 引数の数を合わせる必要がある
  • 自作関数でもコールバック関数を使える

コールバック関数は、JavaScriptの重要な概念です。配列メソッドやイベント処理など、様々な場面で使われます。

コメント