JavaScript | 基礎構文:スコープ・実行コンテキスト - クロージャの基礎(応用はまだ不要)

JavaScript JavaScript
スポンサーリンク

クロージャって何者?

クロージャは、
「外側のスコープの変数を“覚えたまま”生きている関数」 です。

もう少しだけ丁寧に言うと、

ある関数の中で定義された関数が、
その外側の変数にアクセスし続けられる仕組み

これがクロージャです。

難しそうに聞こえるけれど、
実は「レキシカルスコープの自然な結果」にすぎません。
ちゃんと順番に見ていけば、ふわっとした怖さは消えます。


一番シンプルなクロージャの例(まずは感覚から)

外側の変数を“覚えている”関数

まずは、動きを見てください。

function createMessage() {
  const text = "こんにちは";

  function showMessage() {
    console.log(text);
  }

  return showMessage;
}

const fn = createMessage();
fn(); // こんにちは
JavaScript

ここで起きていることを言葉で追うと、

  1. createMessage の中で text を宣言
  2. 同じく中で showMessage を定義(text を使っている)
  3. createMessage() を呼ぶと、showMessage が返ってくる
  4. fn() として showMessage をあとから呼ぶ
  5. そのときも text にアクセスできている

ポイントはここです。

createMessage の実行は終わっているのに、
その中の変数 text がまだ生きていて、
showMessage から見えている。

この「外側の変数を覚えたままの関数」が、クロージャです。


なぜそんなことが起きるのか(レキシカルスコープとの関係)

「どこで定義されたか」を関数が覚えている

さっきのコードを、スコープの観点で見るとこうなります。

function createMessage() {
  const text = "こんにちは"; // createMessage のローカルスコープ

  function showMessage() {
    console.log(text); // 外側の text を参照
  }

  return showMessage;
}
JavaScript

showMessage は、
createMessage の中で定義された関数」です。

JavaScript はレキシカルスコープなので、
関数は「自分が定義されたときの外側スコープ」を覚えます。

だから createMessage() の実行が終わっても、
showMessage は「自分が生まれた場所のスコープ(=text がある場所)」を
一緒に持ち歩いているイメージになります。

ここが重要です。
クロージャは「特別なモンスター」ではなく、
“レキシカルスコープがある世界で、外側の変数を覚えた関数”に名前がついただけ。


クロージャの定番例:カウンター

「外から直接触れないけど、増やせる数」

クロージャの一番有名な例が「カウンター」です。

function createCounter() {
  let count = 0; // 外からは見えない“秘密の変数”

  function increment() {
    count += 1;
    console.log(count);
  }

  return increment;
}

const counter = createCounter();

counter(); // 1
counter(); // 2
counter(); // 3
JavaScript

ここでのポイントを整理します。

countcreateCounter のローカル変数
外から count に直接アクセスすることはできない
increment だけが count を読み書きできる
createCounter() の実行が終わっても、count は消えない

つまり、

count は外からは見えない“プライベートな状態”で、
counter() を呼ぶことでだけ変えられる」

という状態になっています。

これがクロージャの強さです。
「状態を外から守りつつ、必要な操作だけを公開する」
という設計が、関数だけで実現できます。


「外から見えないけど、関数からは見える」という感覚

直接触れない=安全、関数経由=コントロールされた入口

さっきのカウンターをもう少し触ってみます。

const counter = createCounter();

// これは OK
counter(); // 1
counter(); // 2

// これは NG(できない)
console.log(count); // ReferenceError: count is not defined
JavaScript

countcreateCounter の中のローカル変数なので、
外からは存在しないことになっています。

でも counter() を呼ぶと、
increment の中から count にアクセスできる。

ここが重要です。
クロージャは「変数を完全に隠す」のではなく、
“アクセスの仕方を関数経由に限定する”ことで、
状態を安全に扱えるようにしている。

実務では、

「外から勝手に書き換えられたくないけど、
特定の操作だけは許可したい状態」

を扱うときに、クロージャがよく使われます。


もう一つの例:メッセージを覚えた関数を作る

「設定だけ先にしておいて、あとで使う」

次のような関数を考えます。

function createGreeter(name) {
  function greet() {
    console.log("こんにちは、" + name + "さん");
  }
  return greet;
}

const greetTaro = createGreeter("太郎");
const greetHanako = createGreeter("花子");

greetTaro();   // こんにちは、太郎さん
greetHanako(); // こんにちは、花子さん
JavaScript

ここで起きていることは、

createGreeter("太郎") を呼ぶと、
name = "太郎" を覚えた greet が返ってくる

createGreeter("花子") を呼ぶと、
name = "花子" を覚えた別の greet が返ってくる

ということです。

つまり、

「それぞれ違う“外側の状態”を覚えた関数を量産している」

これもクロージャです。
「設定を先にしておいて、あとから使う関数」を作るときに、
クロージャはとても自然に登場します。


クロージャで“混乱しやすいポイント”を先に潰しておく

「いつの値を見ているのか」が分からなくなる問題

よくある混乱は、

「外側の変数があとから変わったら、クロージャはどっちを見るの?」

というものです。

次のコードを見てください。

function create() {
  let value = 1;

  function show() {
    console.log(value);
  }

  value = 2;

  return show;
}

const fn = create();
fn(); // 2
JavaScript

showvalue の「値」ではなく、
「変数そのもの」を覚えています。

だから、
value = 2; と書き換えたあとに fn() を呼ぶと、
その時点の value(2)が表示されます。

ここが重要です。
クロージャが覚えているのは「値のコピー」ではなく「変数への参照」。
だから、外側の変数が変われば、クロージャから見える値も変わる。

この感覚がつかめると、
「なんでこの値になるんだ?」というモヤモヤが減ります。


初心者として「クロージャの基礎」で本当に押さえてほしいこと

ここまでの話を、あえて最低限に絞るとこうです。

クロージャとは、

「外側のスコープの変数を覚えたまま生きている関数」

であり、

それは、

「関数が“定義された場所のスコープ”を覚えている(レキシカルスコープ)」

というルールの自然な結果。

そのおかげで、

外から直接触れられない“プライベートな状態”を持てる
設定だけ先にしておいて、あとから使う関数を作れる

ということができる。

まずはこの2つのパターンを、自分の手で書いてみてください。

// 1. カウンター
function createCounter() {
  let count = 0;
  return function () {
    count++;
    console.log(count);
  };
}

// 2. 名前を覚えた挨拶関数
function createGreeter(name) {
  return function () {
    console.log("こんにちは、" + name + "さん");
  };
}
JavaScript

「外側の変数を覚えた関数を返す」
この形が体に馴染んだ瞬間、
クロージャは“難しい概念”ではなく、
「めちゃくちゃ便利な日常ツール」に変わります。

タイトルとURLをコピーしました