JavaScript | クロージャ比較の図解

JavaScript JavaScript
スポンサーリンク

ここは Function コンストラクタで作った関数通常の関数(関数リテラル/関数式) の「クロージャ(外側のローカル変数を覚えるかどうか)」を視覚的に比較する図解と説明ページです。


目的

  • なぜ new Function(...) で作った関数が外側のローカル変数(クロージャ)を参照できないのかを、実行例で直感的に理解する。

要点の図(概念図)

普通の関数(クロージャあり)

[ outer scope ]                     [ inner function ]
| let secret = 100; |  <-- outer   | return secret + 1 |
|                   |              | (アクセスできる)    |
+-------------------+              +-------------------+
         ↑                               ↑
         |  クロージャ(内側が外側を覚えている)
         +-----------------------------------------+
Function コンストラクタ(クロージャ **なし**)

[ outer scope ]                     [ Function コンストラクタで生成された関数 ]
| let secret = 100; |  <-- outer   | function body as string:  |
|                   |              | 'return typeof secret'    |
+-------------------+              +---------------------------+
         ↑                               ↑
         |  (**参照できない** — ローカルスコープは捕捉されない)
         +-------------------------------------------------------->
                                    (グローバルスコープを探しに行く)

実行例:コードと出力(ステップごと)

例 1:通常の関数(関数式)

function outer() {
  let secret = 100;
  let ok = function() { return secret + 1; };
  console.log(ok()); // 101
}
outer();
JavaScript
  • 解説:okoutersecret覚えている。呼ぶと 101 を返す。

例 2:Function コンストラクタ

function outer() {
  let secret = 100;
  let nok = new Function('return typeof secret === "undefined" ? "no secret" : secret');
  console.log(nok()); // "no secret"
}
outer();
JavaScript
  • 解説:nok は文字列から作られるため outer のローカル変数を見られない。よって secret は未定義として扱われる(もしくはグローバルを探す)。

なぜこうなるか(背景の仕組み)

  1. 通常の関数は定義された場所のスコープ情報を持ち、必要な外側の変数を「クロージャ」として内部に保持できる。これにより、後で呼び出しても外側のローカル変数にアクセスできる。
  2. Function コンストラクタは、関数本体を文字列として受け取り、実行時に新しい関数オブジェクトをグローバルスコープ(または標準環境)を基準にして生成する。つまり “その場のローカルスコープを閉じこめない(captureしない)”。
  3. 結果として Function コンストラクタで生成した関数はクロージャを形成しないため、外側のローカル変数は見えない。代わりに同名のグローバル変数を探す挙動になる。

視覚で覚えるポイント

  • 矢印(→)が外側の変数を参照していることがクロージャ。
  • Function コンストラクタでは矢印が外側のローカルへ伸びない。伸びるのはグローバル層だけ。

実務での注意メモ

  • クロージャを使った設計(関数を返して状態を保持するパターン)はよく使う重要な手法です。Function コンストラクタでは同じ効果が得られないので、クロージャを利用したいなら通常の関数を使いましょう。
  • Function は文字列評価になるため、セキュリティやデバッグの問題があり、使い所は限定されます。

追加の演習(試してみよう)

  1. 上の 2 つの例をブラウザのコンソールで実行して出力の違いを確かめる。
  2. Function コンストラクタの関数を作る前に、window.secret = 200; としてグローバル変数を定義してから nok() を呼んでみるとどうなるか確認する(グローバルが見えるため 200 を返す)。

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