JavaScript | 関数リテラル(無名関数)

JavaScript JavaScript
スポンサーリンク

レベル別 演習問題(関数リテラル/無名関数)

各レベルに問題+期待出力+模範解答(コード)+ステップごとの解説を載せます。ブラウザの 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 は関数を受け取り「測定付きの新しい関数」を返す(デコレータ的)。applyarguments を透過。


問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 は先に固定引数を閉じ込めて、新しい関数で残りを受け取る。applyconcat を活用。


チャレンジ

  • 「無名関数を使って配列をランダムに並べ替える 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

解説

  1. arr.slice() で元の配列をコピー(破壊的変更を避ける)。
  2. 後ろから順にランダムな位置と入れ替え。
  3. [a,b] = [b,a] で簡単に要素を入れ替え。
  4. アロー関数で書くことで短く、無名関数としてすぐ渡せる。

ボーナスチャレンジ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

解説

  1. asyncTask はコールバックで結果を返す従来型。
  2. toPromise(task) で「コールバック関数を Promise 化」して、.then() でチェーン可能にする。
  3. アロー関数でラップし、簡潔に書ける。
  4. 出力例(実行ごとに0.5秒間隔で順番に表示): 処理完了: 1 処理完了: 2 処理完了: 3 最終結果: 4

💡ポイント

  • ボーナス1:配列を扱う際のアルゴリズム理解と無名関数の活用。
  • ボーナス2:非同期処理の基本構造、コールバックから Promise 風への変換。
  • 両方とも無名関数(関数リテラル)またはアロー関数を使うことでコードが短く、読みやすくなる。
タイトルとURLをコピーしました