JavaScript は 関数を普通の値(変数に入れられる・引数に渡せる・戻り値にできる) として扱えます。
この性質を使うと「ある処理が終わったら別の処理を呼ぶ」といった柔軟な書き方ができます。まず用語から押さえましょう。
用語
- コールバック関数(callback):別の関数に渡され、その関数の中で後から呼ばれる関数。
- 高階関数(higher-order function):関数を引数に取ったり、関数を返したりする関数。
基本イメージ(超かんたん)
「関数を手紙だとすると、関数を渡す = 手紙を誰かに渡す。受け取った人(関数)がその手紙(関数)を中で開いて使う」
→ 渡した関数は「後で呼ばれる(コールバック)」。
例1 — 関数を変数に入れる(基礎)
const sayHi = function(name) {
console.log('こんにちは、' + name + 'さん!');
};
sayHi('太郎'); // => こんにちは、太郎さん!
JavaScriptポイント:sayHi は「関数を参照する変数」。sayHi() で実行します。
例2 — 関数を引数として渡す(コールバック)
function greet(name, formatter) {
// formatter は「関数」が来る想定
const message = formatter(name);
console.log(message);
}
function makeHello(n) {
return 'Hello, ' + n + '!';
}
// 関数を渡して呼ぶ
greet('花子', makeHello); // => Hello, 花子!
JavaScript流れ:
greetにnameとformatterを渡すgreetの中でformatter(name)を呼ぶ →makeHelloが実行される
ステップ実行(例2 を図で追う)
呼び出し:greet('花子', makeHello)
greetの引数name = '花子'、formatter = makeHello(関数参照)const message = formatter(name);→makeHello('花子')実行 → 文字列'Hello, 花子!'を返すconsole.log(message)で出力
例3 — その場で関数を作って渡す(匿名関数 / アロー関数)
greet('次郎', (n) => 'やあ、' + n + '!'); // => やあ、次郎!
JavaScriptメリット:小さな処理を使い捨てで素早く書ける。
例4 — 非同期のとき(setTimeout)
非同期処理(時間後に実行するなど)でよく使います。
console.log('開始');
setTimeout(() => {
console.log('3秒後に表示');
}, 3000);
console.log('終了');
// 出力順: '開始' → '終了' → (3秒後)'3秒後に表示'
JavaScriptポイント:setTimeout に渡した関数が「3秒後に呼ばれる」=コールバック。
例5 — 配列メソッドにもコールバックがいっぱい(map, filter, forEach)
const nums = [1, 2, 3, 4];
// map は「要素ごとに関数を呼んで新しい配列を返す」
const doubled = nums.map(x => x * 2); // [2,4,6,8]
// filter は「関数が true の要素だけ残す」
const evens = nums.filter(x => x % 2 === 0); // [2,4]
// forEach は「各要素で関数を実行(戻り値は無視)」
nums.forEach(x => console.log(x));
JavaScriptこれらのメソッドは 高階関数(関数を引数に取る)です。
もう少し高度:高階関数が関数を返す例(カリー化風)
function makeAdder(x) {
// 関数を返す(返された関数は x を覚えている)
return function(y) {
return x + y;
};
}
const add5 = makeAdder(5);
console.log(add5(3)); // => 8
JavaScriptポイント:関数が関数を返すことで、設定済みの関数(設定済みの動作) を作れます。
よくある間違い・注意点
- 関数そのものを渡す vs 実行結果を渡す
setTimeout(doSomething, 1000)は OK(関数参照を渡す)setTimeout(doSomething(), 1000)は NG(関数を即実行してその戻り値を渡す)
- this の取り扱い
- コールバック内の
thisは呼び出し側による。特にメソッドをそのまま渡すとthisが変わることがある →.bind()やアロー関数で対処。
- コールバック内の
- コールバック地獄(callback hell)
- コネクテッドな非同期処理をネストで書くと読みづらくなる。
Promiseやasync/awaitを使うと読みやすくなる。
- コネクテッドな非同期処理をネストで書くと読みづらくなる。
- エラーハンドリング
- 非同期コールバックでは例外が消えることがある(環境による)。Promise の方がエラー伝播が扱いやすい。
実践的な例 — ボタンを押したら処理(ブラウザ)
<button id="btn">押してね</button>
<script>
const btn = document.getElementById('btn');
btn.addEventListener('click', function(event) {
alert('ボタンが押されました!');
});
</script>
JavaScriptaddEventListener に渡している関数が コールバック。イベントが発生したときに呼ばれます。
練習問題
問題1
配列 ['apple','banana','cherry'] を受け取り、各要素の先頭に "果物: " を付けた新しい配列を作る関数 labelFruits(arr, func) を作り、func は要素を受け取って変換した文字列を返すコールバックにしてください。labelFruits は変換後の配列を返すこと。
問題2
waitAndRun(ms, func) という関数を作り、ms ミリ秒待ったあと func() を呼ぶ。実行例を示してください。
問題3(発展)
repeat(n, func) を作り、n 回 func(i) を呼ぶ(i は 0 から n-1 のインデックス)。それを使って 5 回コンソールに Hello 0 … Hello 4 を出力する。
解答例(模範解答)と説明
解答1
function labelFruits(arr, func) {
return arr.map(item => func(item));
}
const fruits = ['apple','banana','cherry'];
const result = labelFruits(fruits, item => '果物: ' + item);
console.log(result);
// => ['果物: apple', '果物: banana', '果物: cherry']
JavaScript説明:labelFruits は内部で map を使い、渡された func を各要素に適用して新しい配列を返します。
解答2
function waitAndRun(ms, func) {
setTimeout(func, ms);
}
waitAndRun(1000, () => console.log('1秒後に実行されました'));
JavaScript説明:setTimeout にコールバック関数を渡すことで指定時間後に実行されます。
解答3
function repeat(n, func) {
for (let i = 0; i < n; i++) {
func(i);
}
}
repeat(5, i => console.log('Hello ' + i));
JavaScript説明:repeat は高階関数。渡した func を複数回(ループ回数分)呼びます。
次のステップ(推奨)
- 短いコードを書いて
console.logで実行順を観察する(同期 vs 非同期 が実感できます)。 - コールバックが複雑になってきたら
Promiseとasync/awaitを学ぶと可読性が大きく向上します。 - 実際のAPI呼び出し(fetch)でコールバック/Promise の挙動を練習してみる。
