JavaScript | レベル別練習問題:関数

JavaScript
スポンサーリンク

では「関数(クロージャ・高階関数・メモ化・this など)」をテーマにした、前回の5問に対応する 追加練習問題(各1〜2問)+ステップ実行付きの丁寧な解説 を紹介します。


1. クロージャの応用 — 2つのカウンターが独立するか?

問題

次のコードの出力を予想し、なぜそうなるかを説明してください。

function makeCounter() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  }
}

const a = makeCounter();
const b = makeCounter();

a();
a();
b();
a();
JavaScript

ステップ実行解説

ステップ実行内容count の所属aのcountbのcount出力
1makeCounter() 呼び出し → クロージャ作成a専用の count=00
2makeCounter() 呼び出し(2回目)b専用の count=000
3a() 実行aの環境を使用101
4a() 実行aの環境202
5b() 実行bの環境211
6a() 実行aの環境313

出力結果

1
2
1
3

解説

  • makeCounter は呼び出すたびに 新しい閉包環境(count変数) を作る。
  • よって、ab独立した内部状態を持ち、互いに干渉しない。
  • クロージャは「状態を持った関数」を生成する便利なパターン。

2. 可変長引数+reduce 応用 — 平均値を求める関数

問題

average(...nums) を定義し、任意の個数の数値の平均値を返す関数を作ってください。
出力例:

console.log(average(2, 4, 6, 8)); // => 5
JavaScript

解答例

function average(...nums) {
  let total = nums.reduce((a, b) => a + b, 0);
  return total / nums.length;
}
JavaScript

ステップ実行解説(average(2,4,6,8))

ステップ変数説明
1nums[2,4,6,8]...で配列化
2reduce1a=0, b=2 → 2初期値0から加算
3reduce2a=2, b=4 → 6累積6
4reduce3a=6, b=6 → 12累積12
5reduce4a=12, b=8 → 20累積20
6total = 20, nums.length=4
7return 20/45平均計算

出力結果

5

解説

  • 可変長引数と reduce の組み合わせは「集計」処理に強い。
  • データ分析系では平均・分散・最大値などもこの流れで実装できる。

3. 高階関数 — 条件付き関数生成

問題

数値が正ならそのまま返し、負なら0を返す「安全加算関数」を生成する高階関数を作ってください。

function makeSafeAdder(limit) {
  return function(x, y) {
    const sum = x + y;
    return sum > limit ? limit : sum;
  }
}

const addMax10 = makeSafeAdder(10);
console.log(addMax10(3, 4));  // => 7
console.log(addMax10(8, 5));  // => 10
JavaScript

ステップ実行(addMax10(8,5))

ステップ関数変数説明
1makeSafeAdder(10) 呼出limit10クロージャ生成
2addMax10(8,5) 呼出x=8, y=5呼出開始
3sum = 13計算
4比較 13 > 10 → true
5return 10出力10

出力結果

7
10

解説

  • クロージャ+条件分岐を組み合わせると、「動的にルールを持った関数」を生成できる。
  • UI制御やバリデーションロジックを動的生成するのにも使える。

4. メモ化再帰の確認 — キャッシュが効いているか見える化

問題

fib(6) の計算中に、キャッシュ利用の様子を出力してください。

function memoFib() {
  const cache = {};
  return function fib(n) {
    if (n in cache) {
      console.log("cache hit:", n);
      return cache[n];
    }
    if (n <= 1) return n;
    cache[n] = fib(n-1) + fib(n-2);
    return cache[n];
  };
}

const fib = memoFib();
console.log(fib(6));
JavaScript

ステップ実行結果(出力例)

cache hit: 2
cache hit: 3
cache hit: 4
cache hit: 5
8

ステップ解説

呼出動作cache の中身
fib(6)初回計算{}
fib(5)fib(4)+fib(3)
fib(4)fib(3)+fib(2)
fib(3)fib(2)+fib(1)
fib(2)fib(1)+fib(0)
以後戻りながらcache登録{2:1,3:2,4:3,5:5}
再利用時「cache hit: n」 出力

解説

  • in cache でキャッシュ確認。
  • 再帰関数の最適化を目で追うのに最適な練習問題。
  • 大きな n(例:40)で試すと速度差が体感できる。

5. thisの理解 — setTimeout内のthisを修正せよ

問題

次のコードの出力を修正して "User: Alice" が表示されるようにしなさい。

const user = {
  name: "Alice",
  greet() {
    setTimeout(function() {
      console.log("User:", this.name);
    }, 1000);
  }
};

user.greet();
JavaScript

① 問題点

  • 通常関数の中では this が失われるため、this.nameundefined になる。

② 修正例(bindを使用)

greet() {
  setTimeout(function() {
    console.log("User:", this.name);
  }.bind(this), 1000);
}
JavaScript

③ 修正版(アロー関数使用)

greet() {
  setTimeout(() => {
    console.log("User:", this.name);
  }, 1000);
}
JavaScript

ステップ実行(アロー関数版)

ステップ説明
1user.greet() 呼び出し。thisuser
2setTimeout に渡すアロー関数が生成される。この時点で thisレキシカル束縛user を記憶。
31秒後、アロー関数実行 → this.name"Alice"
4出力 "User: Alice"

出力

User: Alice

解説

  • アロー関数は外側スコープの this を保持するため、コールバック内で this が変わらない。
  • コールバック処理(イベント・タイマー)で頻出する this 問題の定番解決法。

まとめ

テーマ新問題で学ぶ要点
クロージャ関数ごとに独立した状態を保持できる
可変長+reduce配列操作で集計処理を行うパターン
高階関数条件付き関数生成・動的ロジック構築
メモ化キャッシュ再利用で計算効率アップ
thisbind / アロー関数でのコンテキスト維持
タイトルとURLをコピーしました