【なんで動くの?】クロージャとは?仕組みを一発で理解できる完全ガイド

クロージャとは


プログラミング学習でクロージャは多くの人がつまずく壁です。

  • クロージャが出てきた瞬間に理解が止まる
  • 関数の外の変数が使える理由がわからない
  • 動作は確認できるが仕組みが理解できない

この記事では、クロージャの意味から仕組み、実務での使い方まで体系的に解説します。コード例を使いながら直感的に理解できる内容です。

最後まで読むことで、クロージャを自分の言葉で説明できる状態になります。

クロージャとは?一言でわかる意味

Point

クロージャとは、関数が外側の変数を保持し続ける仕組みのこと。関数の実行が終わった後でも、変数が使える状態を維持します。

もう少し噛み砕くと、クロージャは「変数を覚え続ける関数」です。関数が作られた時点の環境を保存し、その情報を後から使い続けることができます。

この特徴があるため、状態を持つ処理を簡単に作れます。JavaScriptではイベント処理やカウンター機能など、さまざまな場面で活用されています。

なぜ「なんで動くの?」と感じるのか

クロージャが難しいと感じる理由は、直感に反する動きをするからです。

多くの人は「関数の実行が終われば、その中の変数は消える」と考えます。しかしクロージャでは、関数が終了した後も変数が保持されます。この違いが混乱を生みます。

原因をさらに掘り下げると、スコープの仕組みへの理解不足に行き着きます。スコープを正しく理解することが、クロージャ理解の最短ルートです。

クロージャの前提知識:スコープを理解する

クロージャを理解するには、まずスコープの概念を押さえる必要があります。スコープとは「変数が使える範囲」を示すルールです。

JavaScriptには主に3種類のスコープがあります。

スコープの種類 範囲 宣言方法
グローバルスコープ どこからでもアクセス可能 関数の外で宣言
関数スコープ 関数の内部のみ var で宣言
ブロックスコープ {} の内部のみ let / const で宣言

重要なポイントは、内側の関数は外側の変数にアクセスできるという点です。これを「スコープチェーン」と呼びます。

外側の関数で宣言された変数は、内側の関数から参照できます。この仕組みがクロージャの土台になっています。

クロージャの仕組みを分解して理解する

クロージャは「関数」+「変数の記憶」の2つで成り立っています。関数が生成された瞬間に、周囲の環境がセットで保存されます。

1

外側の関数が実行される

変数が作られ、内側の関数が定義される

2

内側の関数が返される

この時点で、外側の変数への参照が一緒に保存される

3

返された関数を実行する

保存された変数にアクセスし、値を読み書きできる

外側の関数の実行は終わっていますが、内側の関数が変数への参照を持ち続けているため、変数は消えません。これがクロージャの正体です。

スコープとクロージャの違い

スコープとクロージャは混同されやすい概念です。違いを整理すると理解が一気に深まります。

項目 スコープ クロージャ
役割 変数のアクセス範囲を決める 変数を保持し続ける
タイミング コードの実行時に決定 関数の作成時に保存
持続性 基本的に一時的 参照がある限り継続
例え 「部屋の壁」(見える範囲を区切る) 「記憶力」(壁の外を覚えている)

簡潔にまとめると、スコープはルール、クロージャは仕組みです。スコープが「どこから見えるか」を決め、クロージャが「見えた情報を覚えておく」を実現します。

【コード例1】クロージャの基本:カウンター

クロージャはコードで確認すると理解しやすいです。最も基本的なカウンターの例を見てみましょう。

JavaScript
function createCounter() {
  let count = 0;       // 外側の変数
  return function () { // 内側の関数(クロージャ)
    count++;
    return count;
  };
}

const counter = createCounter();

console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3

createCounter() の実行は終わっていますが、返された関数が count への参照を保持しています。そのため、関数を呼ぶたびに値が更新されます。

count は外部から直接アクセスできません。関数を通じてのみ操作できる「非公開変数」として機能しています。

【コード例2】プライベート変数の実現

クロージャを使うと、外部から直接触れない変数を作れます。これはカプセル化と呼ばれる設計パターンです。

JavaScript
function createUser(name) {
  let loginCount = 0;  // 外部からアクセス不可

  return {
    getName: function () {
      return name;
    },
    login: function () {
      loginCount++;
      return name + "さんのログイン回数: " + loginCount;
    },
    getLoginCount: function () {
      return loginCount;
    }
  };
}

const user = createUser("田中");
console.log(user.login());         // 田中さんのログイン回数: 1
console.log(user.login());         // 田中さんのログイン回数: 2
console.log(user.getLoginCount()); // 2
// user.loginCount → undefined(直接アクセス不可)

loginCount は関数の外から直接変更できません。login() メソッドを通じてのみ更新されるため、不正な操作を防げます。

【コード例3】設定付き関数の生成

クロージャを使うと、あらかじめ設定を持った関数を作れます。

JavaScript
function createMultiplier(multiplier) {
  return function (number) {
    return number * multiplier;
  };
}

const double = createMultiplier(2);
const triple = createMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15
console.log(double(10)); // 20

createMultiplier(2) が返す関数は、multiplier = 2 を記憶しています。それぞれの関数が独立した環境を持つ点がポイントです。

このパターンは実務で頻繁に使われます。例えば、税率計算や単位変換の関数を動的に生成する場面などです。

クロージャを使う3つのメリット

Merit 01

グローバル変数を減らせる

グローバル変数が増えるとバグの原因になります。クロージャを使えば変数の影響範囲を関数内に限定できるため、他の処理との衝突を防げます。

Merit 02

データを安全に保持できる

外部から直接アクセスできない変数を持てるため、意図しない書き換えを防止できます。APIキーの管理や状態の保護に役立ちます。

Merit 03

状態を持つ処理を簡潔に作れる

カウンターやトグル処理など、「前回の状態を覚えておく」必要がある処理をクラスなしで実装できます。小規模な状態管理に最適です。

クロージャのデメリットと注意点

クロージャは便利ですが、使い方を誤ると問題になる場合があります。

注意点 内容 対策
メモリ消費 変数がメモリに残り続ける 不要になったら参照を null にする
可読性の低下 ネストが深くなると読みにくい 関数名を明確にし、適切にコメントする
デバッグの難しさ 変数の状態が外から見えにくい 開発者ツールのスコープ確認を活用する

大量のクロージャを作る設計は避け、必要な場面だけで使うことが重要です。

よくあるミス:ループとクロージャの罠

クロージャ関連で最も多いバグは、ループ処理で発生します。以下のコードを見てください。

JavaScript(問題のあるコード)
for (var i = 0; i  3; i++) {
  setTimeout(function () {
    console.log(i);
  }, 100);
}
// 期待: 0, 1, 2
// 実際: 3, 3, 3

なぜこうなるのでしょうか。var関数スコープのため、ループ全体で1つの変数 i を共有します。setTimeout の中の関数が実行される時点では、ループはすでに終了しており、i3 になっています。

JavaScript(letで解決)
for (let i = 0; i  3; i++) {
  setTimeout(function () {
    console.log(i);
  }, 100);
}
// 結果: 0, 1, 2(正しく出力される)

letブロックスコープを持つため、ループの繰り返しごとに新しい変数 i が作られます。各クロージャがそれぞれ別の i を参照するため、期待通りの結果になります。

クロージャはどんな場面で使う?

クロージャは実務でも頻繁に登場します。代表的な使用場面を整理します。

用途 具体例 クロージャの役割
カウンター クリック回数の記録 回数を非公開で保持する
イベント処理 ボタンの状態管理 イベント間で状態を共有する
非公開変数 APIキーやトークンの管理 外部から直接アクセスさせない
関数の生成 税率計算・単位変換 設定を記憶した関数を作る
コールバック 非同期処理のデータ受け渡し 呼び出し元の変数を保持する

クロージャとクラスの違い

クロージャとクラスはどちらも「状態を持つ処理」を実現できます。使い分けのポイントを整理します。

項目 クロージャ クラス
構造 関数ベース オブジェクト指向
可読性 小規模なら高い 大規模でも読みやすい
拡張性 限定的 継承・ポリモーフィズムが使える
適した場面 小さな状態管理・ユーティリティ 複雑なオブジェクト設計

小規模な処理にはクロージャ、大規模な設計にはクラスが適しています。プロジェクトの規模や要件に応じて使い分けましょう。

クロージャを使うべき場面と避けるべき場面

使うべき場面

  • 状態を持つ処理が必要なとき
  • 外部から隠したい変数があるとき
  • 設定を記憶した関数を動的に作りたいとき
  • コールバック内で外側の値を利用するとき

避けるべき場面

  • 大規模な状態管理が必要なとき(クラスを検討)
  • チームの可読性を優先する場面
  • メモリ消費を厳しく管理する必要がある場面
  • 単純な処理でクロージャが不要な場面

クロージャが使われる代表的なコードパターン

クロージャには頻出するパターンがあります。型を覚えると応用が利きやすくなります。

パターン 内容 使用例
即時関数(IIFE) 変数を外部に漏らさず閉じ込める ライブラリの初期化処理
カウンター 呼び出し回数を内部で保持する クリック計測・API呼び出し制限
関数ファクトリ 設定付きの関数を動的に生成する 税率計算・バリデーション
コールバック保持 非同期処理と状態を組み合わせる イベントリスナー・タイマー

よくある質問(FAQ)

Q. クロージャは必ず使うべきですか?

必須ではありません。状態の保持や非公開変数が必要な場面で使うと効果的です。単純な処理には不要なので、必要な場面だけで使いましょう。

Q. クロージャとスコープの違いは何ですか?

スコープは変数の「アクセス範囲」を決めるルールです。クロージャはスコープの仕組みを利用して、変数を「保持し続ける」機能です。スコープがルール、クロージャがそのルールを活かした仕組みと考えるとわかりやすいです。

Q. クロージャは難しい概念ですか?

抽象度が高いため最初は難しく感じます。ただし、スコープの基礎を理解してからコード例を動かせば、仕組み自体はシンプルです。「関数が変数を覚えている」とイメージすることがコツです。

Q. クロージャを使いすぎるとどうなりますか?

メモリを消費し続けるため、パフォーマンスに影響する可能性があります。不要になったクロージャの参照は null にして、ガベージコレクションの対象にしましょう。


クロージャを最短で習得する4ステップ

クロージャは理解するだけでなく、使える状態にすることが大切です。以下のステップで進めると効率的に習得できます。

1

スコープの理解を深める

グローバル・関数・ブロックスコープの違いを押さえる。変数の見える範囲と寿命を理解することが土台になります。

2

シンプルなコードを動かす

この記事のカウンター例をブラウザのコンソールで実行してみてください。動作を目で確認することで、仕組みが腑に落ちます。

3

自分でカウンター処理を作る

リセット機能付きカウンターなど、少しだけ応用したものを自作してみましょう。自分で書くことで理解が定着します。

4

実務コードで小さく使う

イベントハンドラや設定値の管理など、実際のプロジェクトでクロージャを取り入れてみてください。実践を通じて応用力が身につきます。

まとめ:クロージャは「記憶する関数」

この記事のポイント

  • クロージャは、関数が外側の変数を保持し続ける仕組み
  • 理解の鍵はスコープ(変数の見える範囲)にある
  • グローバル変数の削減、データ保護、状態管理に有効
  • 使いすぎるとメモリ消費や可読性低下のリスクがある
  • 小規模な状態管理にはクロージャ、大規模にはクラスが適切

クロージャは、理解できれば設計の自由度が大きく広がる概念です。まずはブラウザのコンソールでカウンター処理を動かしてみてください。手を動かすことで、知識がスキルに変わります。

JavaScriptの基礎をもっと学びたい方へ

当サイトではJavaScriptやプログラミングに関するIT用語をわかりやすく解説しています。スコープ、コールバック、非同期処理など、関連する用語もぜひチェックしてみてください。