カテゴリ: JavaScript 更新日: 2025/12/25

JavaScriptのクロージャーとは?仕組みと実践例をやさしく解説

JavaScriptのクロージャーとは?仕組みと実践例をやさしく解説
JavaScriptのクロージャーとは?仕組みと実践例をやさしく解説

先生と生徒の会話形式で理解しよう

生徒

「先生、JavaScriptのクロージャーって聞いたことはあるけど、いまいち何かわからないです。教えてください!」

先生

「クロージャーは少し難しい言葉ですが、簡単にいうと『関数が自分の外側の変数を覚えて使い続ける仕組み』のことです。」

生徒

「関数が変数を覚える?どういうことですか?」

先生

「例えば、お菓子の箱を開けて中身を見て、その箱を閉じても、中身のことを忘れないようなイメージですね。関数は箱で、中の変数が中身です。」

生徒

「なるほど。クロージャーはプログラムのどんなときに使うんですか?」

先生

「よく使うのは、変数を隠して安全に使いたいときや、関数の中に状態を持たせたいときです。実践的な例もあとで紹介しますね。」

1. クロージャーとは?

1. クロージャーとは?
1. クロージャーとは?

クロージャーとは、JavaScriptで関数が作られたときの周囲の環境(変数の状態)を覚え続ける仕組みのことです。 一度実行が終わった関数でも、その中で参照していた変数を、あとから呼び出された関数が使い続けられる点が特徴です。

通常であれば、関数の処理が終わると中の変数は役目を終えます。 しかしクロージャーが関係すると、「もう終わったはずの関数の変数」が、別の関数の中で生き続けます。 この少し不思議な動きが、クロージャーを難しく感じさせる理由でもあります。

プログラミング未経験の方は、「関数が自分専用のメモを持っている」と考えるとイメージしやすいでしょう。 箱の中にメモを入れておき、その箱を閉じても、あとでまた同じメモを取り出せるような感覚です。


function makeMessage() {
  let message = "こんにちは";
  return function() {
    console.log(message);
  };
}

const showMessage = makeMessage();
showMessage(); // 「こんにちは」と表示される

この例では、makeMessage関数の中で定義されたmessageという変数を、 戻り値の関数が覚えています。 そのため、makeMessageの処理が終わったあとでも、 showMessage()を呼び出すと同じ文字が表示されます。 これがクロージャーの基本的な考え方です。

2. クロージャーの基本的な仕組み

2. クロージャーの基本的な仕組み
2. クロージャーの基本的な仕組み

通常、関数の中で使う変数は、関数の処理が終わると役目を終えて消えていきます。 だから「関数の外からは中の変数を使えない」というのが基本です。

ところがクロージャーが関係すると、関数が終わったあとでも変数が残っているように見える状況が作れます。 正確には、内側の関数が外側の変数を参照している限り、その変数の状態が保たれます。 「外側の変数を、内側の関数がポケットに入れて持ち歩く」ようなイメージを持つと理解しやすいです。

まずは一番よく使われる形として、外側の関数が内側の関数を返す例を見てみましょう。 内側の関数を呼び出すたびに、外側の変数が変化していくのがポイントです。


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

const counter = outer();
counter(); // 1
counter(); // 2
counter(); // 3

この例では、outer関数の中にあるcountという変数を、inner関数が参照しています。 そのため、outer()の実行が終わっても、innerが残っている限りcountも一緒に残ります。 そしてcounter()を呼ぶたびに、同じcountが更新されていくので、数が1、2、3…と増えていくわけです。

もう少し短い例で、「覚えている」感じを確かめてみます。 たとえば、countの値を表示するだけのメソッドでも、同じ仕組みで状態が保たれます。


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

const c = makeCounter();
console.log(c()); // 1
console.log(c()); // 2

このようにクロージャーの基本は、「外側の変数を参照している内側の関数があると、その変数の状態が残る」という点にあります。 まずはこの仕組みが分かると、クロージャーの動きがぐっと読みやすくなります。

3. なぜクロージャーが便利なのか?

3. なぜクロージャーが便利なのか?
3. なぜクロージャーが便利なのか?

クロージャーがあると、変数を外から直接触られたくないときに安全に隠せます。

また、関数の中でデータを保存しておくことができるので、プログラムをきれいに書けます。

たとえば、ユーザーのログイン回数やクリック数を数えるときに便利です。

4. 実践例:カウンターを作ってみよう

4. 実践例:カウンターを作ってみよう
4. 実践例:カウンターを作ってみよう

クロージャーを使ってカウンター(数を数える仕組み)を作ってみます。


function createCounter() {
  let count = 0;
  return function() {
    count++;
    return count;
  };
}
const counter1 = createCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2

const counter2 = createCounter();
console.log(counter2()); // 1
console.log(counter2()); // 2

このように、createCounterを呼ぶと、それぞれ独立したカウンターが作れます。

関数の中のcount変数は外から直接触れませんが、戻り値の関数を通じて増やしたり読み取ったりできます。

5. クロージャーを使うときの注意点

5. クロージャーを使うときの注意点
5. クロージャーを使うときの注意点

クロージャーは便利ですが、使いすぎるとメモリをたくさん使ってしまうことがあります。

また、変数の状態が長く残るので、意図しない動きになることもあります。

ですから、必要なときだけ使うようにしましょう。

6. クロージャーのポイント整理

6. クロージャーのポイント整理
6. クロージャーのポイント整理

ここまで見てきたように、JavaScriptのクロージャーは 「関数が自分の外側にある変数を覚えたまま動き続ける仕組み」です。 一度実行が終わった関数の中の変数でも、別の関数から参照されていれば、 その値は失われずに保持されます。

この仕組みを使うことで、外から直接触れない変数を作れたり、 関数の中に「状態」を持たせたりできます。 グローバル変数をむやみに増やさずに済むため、 プログラム全体が読みやすく、管理しやすくなるのも大きなメリットです。

初心者の方は、「クロージャー=難しい特別な機能」と考えがちですが、 実際は関数と変数の関係を少し深く理解したものにすぎません。 外側の変数を使っている内側の関数があれば、 そこにクロージャーが生まれている、と覚えておくと混乱しにくいでしょう。


function createGreeting() {
  let message = "こんにちは";
  return function(name) {
    console.log(message + "、" + name + "さん");
  };
}

const greet = createGreeting();
greet("太郎");
greet("花子");

この例では、messageという変数を内側の関数が覚えています。 そのため、名前だけを渡すシンプルな呼び出しでも、 同じあいさつ文を何度も使い回せます。 これもクロージャーによって状態が保たれているからこそ実現できる動きです。

まずは「変数を覚えている関数がある」という感覚をつかむことが大切です。 カウンターや簡単なあいさつの例を何度か書いてみると、 クロージャーの考え方が自然と身についていきます。

まとめ

まとめ
まとめ

クロージャーの仕組みを振り返る

ここまでの記事では、JavaScriptのクロージャーについて、基本的な考え方から実践的な使い方までを順番に見てきました。 クロージャーは一見すると難しい言葉ですが、仕組みそのものはとてもシンプルです。 「関数が作られたときの変数の状態を覚え続ける」という特徴を持っていることが、クロージャーの本質だといえます。

通常の関数では、関数の実行が終わると中で使っていた変数は役目を終えます。 しかし、関数の中で別の関数を作り、その関数を外に返すと、内側の関数は外側の変数を覚えたまま動き続けます。 この「覚えている状態」がクロージャーです。

クロージャーが役立つ理由

クロージャーが便利なのは、変数を安全に管理できる点にあります。 グローバル変数のように誰からでも触れる状態ではなく、必要な関数からだけアクセスできるため、 プログラム全体の見通しが良くなります。

また、関数の中に状態を持たせられるため、「前回の結果を覚えて次に活かす」といった処理が自然に書けます。 カウンターの例のように、呼び出すたびに値が変わる仕組みは、クロージャーを使うことでとても簡潔に表現できます。

サンプルで再確認してみよう

ここでもう一度、クロージャーの動きを確認できるシンプルな例を見てみましょう。 この例では、文字列を覚えて表示する関数を作っています。


function createMessage(message) {
  return function() {
    console.log(message);
  };
}

const hello = createMessage("こんにちは");
const thanks = createMessage("ありがとう");

hello();
thanks();

このコードでは、createMessageの中で受け取った文字列が、 返された関数の中で使われ続けています。 それぞれの関数が、自分専用の文字列を覚えている点がクロージャーの特徴です。

クロージャーを使うときの心構え

クロージャーはとても強力な仕組みですが、便利だからといって何でもクロージャーで書くのはおすすめできません。 変数の状態が長く残るため、意図しない動作やメモリの無駄につながることもあります。

「変数を隠したい」「状態を保持したい」といった目的がはっきりしている場面で使うことで、 クロージャーは本来の力を発揮します。 まずは小さな例で慣れてから、実際のアプリケーションに取り入れていくと理解が深まりやすいでしょう。

先生と生徒の振り返り会話

生徒

「最初はクロージャーって難しそうだと思っていましたけど、 関数が変数を覚えている仕組みだと考えると、少しイメージできました。」

先生

「それで大丈夫ですよ。大切なのは、動きをイメージできることです。 完璧に説明できなくても、使いどころが分かれば十分です。」

生徒

「カウンターやメッセージの例を見ると、 同じ関数から別々の状態を持てるのが便利だと感じました。」

先生

「その気づきはとても良いですね。 クロージャーは状態を持つ関数を作れる点が一番の強みです。」

生徒

「これからコードを書くときに、 変数をどこに置くか考えるのが少し楽しくなりそうです。」

先生

「その感覚を大切にしてください。 クロージャーはJavaScriptらしさを理解する大きな一歩になりますよ。」

カテゴリの一覧へ
新着記事
New1
TypeScript
TypeScriptでパスエイリアスを設定する方法!baseUrlとpathsでコードをスッキリ整理
New2
JavaScript
JavaScriptのfor文の書き方を初心者向けにやさしく解説
New3
JavaScript
JavaScriptの関数でよくあるエラーとその解決法まとめ
New4
JavaScript
JavaScriptのイベント処理でよくあるエラーとその対処法
人気記事
No.1
Java&Spring記事人気No1
JavaScript
JavaScriptのインストール方法まとめ!Windows・Mac・Linux別にステップ解説
No.2
Java&Spring記事人気No2
JavaScript
JavaScriptのマウスイベントの使い方(click, mouseoverなど)
No.3
Java&Spring記事人気No3
JavaScript
JavaScriptのtoStringとString関数の違いを初心者向けに解説
No.4
Java&Spring記事人気No4
JavaScript
JavaScriptの純粋関数(pure function)と副作用の違いを理解しよう
No.5
Java&Spring記事人気No5
JavaScript
JavaScriptプログラムの実行方法まとめ!ブラウザ・Node.js・コンソールの使い方
No.6
Java&Spring記事人気No6
JavaScript
JavaScriptで文字列をforループで1文字ずつ処理する方法!初心者向け解説
No.7
Java&Spring記事人気No7
TypeScript
TypeScript学習におすすめの無料教材・リファレンスサイト【初心者向け】
No.8
Java&Spring記事人気No8
TypeScript
TypeScriptの始め方:開発環境の構築手順【初心者向け】