3
1

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] クロージャ (Closure) とは

Last updated at Posted at 2025-10-15

概要

クロージャ(Closure) とは、
「関数が定義されたときのスコープ(外部変数)を保持し続ける仕組み」です。

つまり、

  • 関数が“どこで定義されたか”を基準にスコープが決まる
  • 定義時の外部変数を記憶し、関数の実行時にも参照できる
  • React Hooks(useEffect, useCallback など)や非同期処理(setTimeout など)にも必須の概念

です。

目次

基本構文

function outer() {
  let message = "Hello Closure";

  function inner() {
    console.log(message); // 外側スコープの変数を参照
  }

  return inner;
}

const fn = outer();
fn(); // "Hello Closure"

解説:

  • inner 関数は、outer 関数のスコープ内で定義されたため、outer が実行済み(通常なら消える変数)でも、message を保持できる。
  • この「スコープを保持し続ける関数」が クロージャ

setTimeoutとクロージャ

function logLater() {
  let message = "Hello";

  setTimeout(function () {
    console.log(message); // ← クロージャで message を保持
  }, 1000);
}

logLater();

ポイント:

  • setTimeout の中の無名関数は、非同期的に呼び出される。
  • しかし、外側の message は破棄されず保持されている。
  • これが「クロージャによるスコープの保持」。

React Hooksとクロージャ

React Hooks(特に useEffectuseCallbackuseMemo)も
クロージャの性質を前提 に動作しています。

function MyComponent() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setTimeout(() => {
      console.log(count); // ← クロージャで count を保持
    }, 1000);

    return () => clearTimeout(timer);
  }, []);
}

解説:

  • setTimeout の中の関数は、count の「初期値」を保持したまま動作。
  • useEffect の依存配列が [] の場合、再レンダー時も新しい count を参照しない。
  • これがいわゆる Stale Closure(古いクロージャ)問題 の原因。

活用例

1. プライベート変数の実現

クロージャを使うことで、外部から直接アクセスできない「プライベート変数」を作れます。

function createUser(name) {
  let secret = "classified";
  return {
    getName: () => name,
    getSecret: () => secret,
    setSecret: (s) => { secret = s; }
  };
}

const user = createUser("Alice");
console.log(user.getName());   // Alice
console.log(user.getSecret()); // classified
user.setSecret("updated");
console.log(user.getSecret()); // updated

用途:
ユーザー情報・トークン・設定値などを外部から隠したいとき。
クラス構文を使わずに「カプセル化」を実現。

2. 関数ファクトリ

クロージャを使うことで、状態を持つ関数を量産できます。

function createValidator(type) {
  return function (value) {
    if (type === "email") return value.includes("@");
    if (type === "number") return !isNaN(value);
    return false;
  };
}

const emailValidator = createValidator("email");
const numberValidator = createValidator("number");

console.log(emailValidator("test@mail.com")); // true
console.log(numberValidator("abc")); // false

用途:
バリデーションやフィルタ関数の生成。
メリット: 各関数が独立した状態を持てる。

3. 非同期処理での状態保持

非同期処理の中で変数を保持するのもクロージャの代表的な用途です。

function delayLog(message) {
  setTimeout(() => {
    console.log(message); // クロージャで message を保持
  }, 1000);
}

delayLog("Hello Async!");

用途:
ログ・UI更新・トラッキング処理など、非同期イベント時に値を保持。

4. Reactでの利用とStale Closure

useEffect(() => {
  const timer = setTimeout(() => {
    console.log(count); // count はクロージャで保持される
  }, 1000);

  return () => clearTimeout(timer);
}, []);

用途:
ステートのスナップショット保持やイベントの最適化。

注意:
count の最新値を取得したい場合は、依存配列に count を追加する必要があります。
古い値を保持したままになると、Stale Closure と呼ばれるバグの原因になります。


まとめ

シーン クロージャの役割 メリット
プライベート変数 外部アクセスの遮断 データの安全性向上
関数ファクトリ 状態を持つ関数生成 コード再利用性
非同期処理 遅延実行時の状態保持 非同期安全性
React Hooks ステートとイベント整合性 副作用の正確な制御

クロージャは「隠す・保持する・分離する」ための設計パターン。

参考リンク

3
1
2

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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?