レベル別 演習問題(関数リテラル/無名関数)
各レベルに問題+期待出力+模範解答(コード)+ステップごとの解説を載せます。ブラウザの Console や Node.js でコピペして動かしてみてください。手を動かすのが一番覚えます!
レベル1:入門(基礎を確認)
短くて取り組みやすい問題を4問。
問1-1:挨拶関数
課題:greet に関数リテラルを代入して、引数 name を受け取り こんにちは、<name>! を console.log する。
期待出力:こんにちは、花子!
模範解答
let greet = function(name) {
console.log("こんにちは、" + name + "!");
};
greet("花子");
JavaScript解説:変数 greet に無名関数を代入。greet("花子") で実行。
問1-2:足し算(戻り値)
課題:sum に 2つの数を受け取って合計を返す関数を代入し、3 と 7 を渡して結果を表示。
期待出力:10
模範解答
let sum = function(a, b) {
return a + b;
};
console.log(sum(3, 7)); // 10
JavaScript解説:return で値を返し、console.log で表示。
問1-3:偶数判定(真偽値)
課題:isEven に整数を受け取り偶数なら true を返す関数を代入。4 を渡して結果表示。
期待出力:true
模範解答
let isEven = function(n) {
return n % 2 === 0;
};
console.log(isEven(4)); // true
JavaScript解説:剰余 % を使って偶数判定。
問1-4:即時実行風(定義してすぐ呼ぶ)
課題:message を受け取り即座に console.log する無名関数を作り、すぐに "今すぐ実行" を渡して実行する(代入してから呼ぶパターン)。
期待出力:今すぐ実行
模範解答
let printer = function(msg) {
console.log(msg);
};
printer("今すぐ実行");
JavaScript解説:関数リテラルを変数に入れてすぐ呼ぶ基本パターン。
レベル2:中級(コールバック/配列操作)
関数を引数に渡すパターンや少しロジックのある問題。
問2-1:operate(汎用)
課題:operate(a, b, func) を作る。func は (a,b) を受けて何らかの処理をする関数。operate(8, 3, ...) で引き算をする無名関数を渡して結果を表示。
期待出力:5
模範解答
function operate(a, b, func) {
return func(a, b);
}
let result = operate(8, 3, function(x, y) {
return x - y;
});
console.log(result); // 5
JavaScript解説:operate が受け取った関数を呼び出して結果を返す(コールバックの基本)。
問2-2:配列の filter を自作(関数を渡す)
課題:filterWith(arr, predicate) を作る。predicate は要素を受けて真偽を返す関数。配列 [1,2,3,4,5] から偶数だけ取り出す。
期待出力:[2, 4]
模範解答
function filterWith(arr, predicate) {
let res = [];
for (let i = 0; i < arr.length; i++) {
if (predicate(arr[i])) res.push(arr[i]);
}
return res;
}
let nums = [1,2,3,4,5];
let evens = filterWith(nums, function(n) { return n % 2 === 0; });
console.log(evens); // [2, 4]
JavaScript解説:高階関数(関数を引数に取る関数)の練習。渡す判定関数で挙動を変えられる。
問2-3:コールバックでエラーハンドリング風
課題:fetchMock(successCallback, errorCallback) を作る(実際の非同期ではなく同期シミュレーション)。true を返す仮の成功判定に基づき、成功時は successCallback("OK")、失敗時は errorCallback("NG") を呼ぶ。成功ケースを実行。
期待出力:成功: OK
模範解答
function fetchMock(successCallback, errorCallback) {
let isSuccess = true; // 今回は成功をシミュレート
if (isSuccess) successCallback("OK");
else errorCallback("NG");
}
fetchMock(
function(data) { console.log("成功: " + data); },
function(err) { console.log("失敗: " + err); }
);
// → 成功: OK
JavaScript解説:コールバックを成功・失敗で分けるパターン(非同期処理の模倣)。
問2-4:map を自作(変換関数を渡す)
課題:mapWith(arr, transform) を作り、[2,5,10] を受け取り各要素を 3 倍にした配列を返す。
期待出力:[6, 15, 30]
模範解答
function mapWith(arr, transform) {
let res = [];
for (let i = 0; i < arr.length; i++) {
res.push(transform(arr[i]));
}
return res;
}
let tripled = mapWith([2,5,10], function(n) { return n * 3; });
console.log(tripled); // [6, 15, 30]
JavaScript解説:transform を使って要素を変換する基本パターン。
レベル3:上級(実践的・発展)
無名関数の応用や少し複雑なロジック、名前付き関数式の使用。
問3-1:イベント風コールバック(シンプルな実装)
課題:簡易イベントシステム on(eventName, handler) と emit(eventName, data) を作る。on で handler(関数)を登録し、emit で登録された handler を呼ぶ。例として greet イベントに登録して emit("greet", "太郎") を実行して こんにちは、太郎! を出す。
期待出力:こんにちは、太郎!
模範解答
let events = {};
function on(eventName, handler) {
if (!events[eventName]) events[eventName] = [];
events[eventName].push(handler);
}
function emit(eventName, data) {
let handlers = events[eventName] || [];
for (let i = 0; i < handlers.length; i++) {
handlers[i](data);
}
}
// 登録と発火
on("greet", function(name) {
console.log("こんにちは、" + name + "!");
});
emit("greet", "太郎"); // → こんにちは、太郎!
JavaScript解説:イベント登録(配列に handler を保持)→ 発火時に全 handler を実行する基本。
問3-2:名前付き関数式で再帰(スタック深さ注意)
課題:fact に名前付き関数式を使って階乗を計算する関数を代入(再帰利用)。fact(6) を表示。
期待出力:720
模範解答
let fact = function inner(n) {
if (n <= 1) return 1;
return n * inner(n - 1);
};
console.log(fact(6)); // 720
JavaScript解説:名前付き関数式 (inner) を関数内部で再帰的に使える。外部からは inner にアクセス不可(fact を使う)。
計算チェック(手で確かめる):
6! = 6×5×4×3×2×1 = 720。正しいです。
問3-3:高階関数でデコレータ風(関数をラップ)
課題:timeIt(func) を作り、渡した func を呼ぶ前後で時間を測って実行時間を console.log する(簡易)。例として slow(n) をラップして slow(1000000) を実行。
期待出力:Result: <値> と Elapsed: <ミリ秒> ms(実行時間はマシンにより変わる)
模範解答
function timeIt(func) {
return function() {
let start = Date.now();
let result = func.apply(this, arguments); // 引数をそのまま渡す
let end = Date.now();
console.log("Elapsed: " + (end - start) + " ms");
return result;
};
}
function slow(n) {
let s = 0;
for (let i = 0; i < n; i++) s += i;
return s;
}
let timedSlow = timeIt(slow);
console.log("Result:", timedSlow(100000)); // 結果と経過時間が出る
JavaScript解説:timeIt は関数を受け取り「測定付きの新しい関数」を返す(デコレータ的)。apply で arguments を透過。
問3-4:部分適用(partial)を自作
課題:partial(func, ...fixedArgs) を作り、固定引数を先に与えた新しい関数を返す。例:add(a,b,c) に partial(add, 10) を適用して残りの引数を渡す。
期待出力:25(10 + 7 + 8)
模範解答
function partial(func) {
let fixed = Array.prototype.slice.call(arguments, 1);
return function() {
let rest = Array.prototype.slice.call(arguments);
return func.apply(this, fixed.concat(rest));
};
}
function add(a, b, c) {
return a + b + c;
}
let add10 = partial(add, 10);
console.log(add10(7, 8)); // 25
JavaScript解説:partial は先に固定引数を閉じ込めて、新しい関数で残りを受け取る。apply と concat を活用。
チャレンジ
- 「無名関数を使って配列をランダムに並べ替える shuffle 関数を作る」
- 「コールバック地獄(callback hell)を避けるための Promise 風ラッパーを作る(擬似非同期)」
やってみたい方は、どちらか一つを選んでください。模範解答を作ります!
模範解答
ボーナスチャレンジ1:無名関数で配列をランダムに並べ替える shuffle 関数
課題
- 配列
[1,2,3,4,5]をランダムに並べ替えるshuffle関数を作る。 - 無名関数(関数リテラル)またはアロー関数で実装。
模範解答(Fisher–Yates アルゴリズム)
let shuffle = arr => {
let result = arr.slice(); // 元の配列をコピー
for (let i = result.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1)); // 0〜iのランダム整数
[result[i], result[j]] = [result[j], result[i]]; // 入れ替え
}
return result;
};
let numbers = [1,2,3,4,5];
console.log(shuffle(numbers)); // → 例: [3,1,5,2,4]
JavaScript解説
arr.slice()で元の配列をコピー(破壊的変更を避ける)。- 後ろから順にランダムな位置と入れ替え。
[a,b] = [b,a]で簡単に要素を入れ替え。- アロー関数で書くことで短く、無名関数としてすぐ渡せる。
ボーナスチャレンジ2:コールバック地獄(callback hell)を避ける Promise 風ラッパー
課題
- 擬似的に非同期処理
asyncTask(data, callback)を作る。 - Promise 風にラップしてチェーンできるようにする。
模範解答
// 擬似非同期処理(setTimeoutでシミュレーション)
function asyncTask(data, callback) {
setTimeout(() => {
console.log("処理完了: " + data);
callback(data + 1);
}, 500); // 0.5秒後に実行
}
// Promise風ラッパー
function toPromise(task) {
return (data) => new Promise(resolve => {
task(data, resolve);
});
}
// チェーン実行
toPromise(asyncTask)(1)
.then(toPromise(asyncTask))
.then(toPromise(asyncTask))
.then(result => console.log("最終結果:", result));
JavaScript解説
asyncTaskはコールバックで結果を返す従来型。toPromise(task)で「コールバック関数を Promise 化」して、.then()でチェーン可能にする。- アロー関数でラップし、簡潔に書ける。
- 出力例(実行ごとに0.5秒間隔で順番に表示):
処理完了: 1 処理完了: 2 処理完了: 3 最終結果: 4
💡ポイント
- ボーナス1:配列を扱う際のアルゴリズム理解と無名関数の活用。
- ボーナス2:非同期処理の基本構造、コールバックから Promise 風への変換。
- 両方とも無名関数(関数リテラル)またはアロー関数を使うことでコードが短く、読みやすくなる。
