ここは 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- 解説:
okはouterのsecretを 覚えている。呼ぶと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は未定義として扱われる(もしくはグローバルを探す)。
なぜこうなるか(背景の仕組み)
- 通常の関数は定義された場所のスコープ情報を持ち、必要な外側の変数を「クロージャ」として内部に保持できる。これにより、後で呼び出しても外側のローカル変数にアクセスできる。
- Function コンストラクタは、関数本体を文字列として受け取り、実行時に新しい関数オブジェクトをグローバルスコープ(または標準環境)を基準にして生成する。つまり “その場のローカルスコープを閉じこめない(captureしない)”。
- 結果として Function コンストラクタで生成した関数はクロージャを形成しないため、外側のローカル変数は見えない。代わりに同名のグローバル変数を探す挙動になる。
視覚で覚えるポイント
- 矢印(→)が外側の変数を参照していることがクロージャ。
- Function コンストラクタでは矢印が外側のローカルへ伸びない。伸びるのはグローバル層だけ。
実務での注意メモ
- クロージャを使った設計(関数を返して状態を保持するパターン)はよく使う重要な手法です。
Functionコンストラクタでは同じ効果が得られないので、クロージャを利用したいなら通常の関数を使いましょう。 Functionは文字列評価になるため、セキュリティやデバッグの問題があり、使い所は限定されます。
追加の演習(試してみよう)
- 上の 2 つの例をブラウザのコンソールで実行して出力の違いを確かめる。
Functionコンストラクタの関数を作る前に、window.secret = 200;としてグローバル変数を定義してからnok()を呼んでみるとどうなるか確認する(グローバルが見えるため200を返す)。
