0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【JavaScript】クロージャを理解する

Posted at

クロージャとは

クロージャとは、関数とその関数が作られた時の環境(スコープ)を組み合わせたものです。

もっと簡単に言うと、関数が自分の外側にある変数を記憶し続ける仕組みのことです。

前提知識:スコープの基本

クロージャを理解する前に、スコープについて簡単に確認しましょう。

スコープとは変数が参照できる範囲のことで、JavaScriptでは内側から外側の変数にアクセスできます。

const global = 'グローバル';

function outer() {
  const outerVar = '外側';
  
  function inner() {
    const innerVar = '内側';
    console.log(innerVar);  // '内側' - 自分のスコープ
    console.log(outerVar);  // '外側' - 親のスコープ
    console.log(global);    // 'グローバル' - グローバルスコープ
  }
  
  inner();
}

outer();

通常、関数の実行が終わると、その関数内で宣言した変数はメモリから削除されます。しかし、クロージャを使うと変数を保持し続けることができるんです。

クロージャの基本例

最もシンプルなクロージャの例を見てみましょう。

function createGreeting(name) {
  return function() {
    console.log(`こんにちは、${name}さん`);
  };
}

const greetTaro = createGreeting('太郎');
const greetHanako = createGreeting('花子');

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

ここで注目すべきポイントは、createGreetingの実行が終わった後も、返された関数がnameという変数にアクセスできていることです。

実行の流れ

なぜ変数が保持されるのか

JavaScriptのメモリ管理は、以下のルールで動作します。

「どこかから参照されている変数は、メモリに残り続ける」

function createCounter() {
  let count = 0;  // ①この変数が作成される
  
  return function() {  // ②この関数がcountを参照している
    count++;
    return count;
  };
}

const counter = createCounter();
// createCounterの実行は終わったが、
// 返された関数がcountを使っているので、
// countはメモリに残り続ける

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

メモリ管理の仕組み

クロージャの実用例

例1: カウンター機能

function createCounter() {
  let count = 0;
  
  return {
    increment: function() {
      count++;
      return count;
    },
    decrement: function() {
      count--;
      return count;
    },
    getValue: function() {
      return count;
    }
  };
}

const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
console.log(counter.getValue());  // 1

count変数は外部から直接アクセスできず、用意されたメソッドを通してのみ操作できます。これをプライベート変数と呼びます。

例2: イベントハンドラ

function setupButtons() {
  const buttons = document.querySelectorAll('.btn');
  
  buttons.forEach(function(button, index) {
    button.addEventListener('click', function() {
      console.log(`ボタン${index}がクリックされました`);
    });
  });
}

setupButtons();

各イベントハンドラは、それぞれ異なるindexの値を記憶しています。これもクロージャの働きですね。

例3: 関数ファクトリー

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

同じ関数構造でも、保持しているmultiplierの値が異なるため、異なる動作をします。

クロージャの動作を図で理解する

複数のクロージャが作られた場合、それぞれ独立した変数を持ちます。

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

const counter1 = createCounter();
const counter2 = createCounter();

console.log(counter1()); // 1
console.log(counter1()); // 2
console.log(counter2()); // 1  ← counter1とは別のcount

まとめ

クロージャの重要なポイントは以下の3つです。

変数の保持
関数が外側の変数を参照している場合、その変数は関数が存在する限り保持される

独立性
各クロージャは独自の変数のコピーを持ち、互いに影響しない

用途
プライベート変数、状態の保持、関数ファクトリーなど、幅広い用途で活用できる

クロージャはJavaScriptの強力な機能の一つです。最初は難しく感じるかもしれませんが、「関数が変数を記憶し続ける仕組み」と理解すれば、徐々に使いこなせるようになります。実際にコードを書いて、動作を確認しながら学んでいきましょう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?