JavaScript | Function コンストラクタを使って関数を定義する

JavaScript JavaScript
スポンサーリンク

Function コンストラクタは「文字列から関数を作る」ための特殊な方法です。普段はあまり使わないのでまずは「どう動くか」を分かりやすく丁寧に説明し、最後に実践的な例題と演習問題(解答付き)を置きます。


1. 基本のイメージ(超やさしく)

通常の関数はこんな風に書きますよね:

function add(a, b) {
  return a + b;
}
JavaScript

Function コンストラクタはこれと同じ関数を文字列で作ります:

let add = new Function('a', 'b', 'return a + b');
console.log(add(2, 3)); // 5
JavaScript

ポイント:new Function(arg1Name, arg2Name, ..., functionBodyAsString) の形で作る。最後の引数が関数の中身(文字列)です。


2. 書き方のバリエーション

  • 引数なし:
let f = new Function('return 42');
console.log(f()); // 42
JavaScript
  • new を省略しても動く(ただし new を書くのが分かりやすい):
let g = Function('x', 'y', 'return x * y');
console.log(g(4,5)); // 20
JavaScript
  • 関数本体に複数行を書く場合はセミコロンで区切るか文字列をそのまま渡す:
let h = new Function(
  'x',
  'y',
  'let s = x + y; console.log("計算中:", s); return s;'
);
h(1,2); // "計算中: 3" と表示して 3 を返す
JavaScript

3. Function と通常の関数の違い(重要)

  1. 関数本体が文字列
    → コードが文字列として作られるので、動的に作れる一方で読みにくい。
  2. スコープ(変数の参照範囲)が違う
    Function で作った関数は、その場のローカル変数を捕捉(クロージャ)できません。外側の関数のローカル変数にアクセスできず、グローバルや外部スコープしか見えないイメージです。
    例(あとで詳しく説明します)。
  3. セキュリティと可読性
    文字列を実行するので、eval と同様に外部入力を直接渡すと危険。またデバッグが難しい。
  4. this の挙動
    Function で作った関数は通常の関数と同じ this(呼び出し時に決まる)で、アロー関数のような レキシカルな this(束縛) にはできません。
  5. パフォーマンス
    実行時にパースされるため、頻繁に作ると遅いことがある(通常は問題にならないが注意)。

4. スコープ(クロージャ)に関する具体例

ローカル変数を捕捉できない点を示します。

function outer() {
  let secret = 100;
  // 通常の関数(クロージャ):
  let ok = function() {
    return secret + 1; // outer の secret にアクセスできる
  };

  // Function コンストラクタで作った関数:
  let nok = new Function('return typeof secret === "undefined" ? "no secret" : secret');

  console.log(ok());   // 101
  console.log(nok());  // "no secret" (secret は見えない)
}
outer();
JavaScript

解説:ok は outer の secret を覚えている(クロージャ)。一方 nok は文字列から作られるため、そのローカルスコープを参照できず secret は見えません(グローバルを探しに行く)。


5. いつ使う?(実用的な場面と注意)

使える場面(稀)

  • ユーザーが入力した式を評価する電卓のような小さなツール(ただし安全対策が必須)
  • 動的にコードを生成する高度ツール(自動生成スクリプトなど)
    → ただし安全性・可読性・デバッグ性の観点から普通は避けるべき。ほとんどのケースでは関数宣言、関数式、アロー関数を使いましょう。

絶対に気をつけること

  • 外部から来た文字列(ユーザー入力など)をそのまま渡すと 任意のコード実行(危険) につながる。
  • デバッグが難しい(ソースのどの行で何が起きたか追いづらい)。

6. 実例(ステップごとに解説)

例 A:足し算関数を作る(基本)

let add = new Function('a', 'b', 'return a + b');
console.log(add(3,4)); // 7
JavaScript
  • 'a', 'b' が引数名
  • 最後の文字列は関数の中身(return a + b

例 B:計算式を文字列から評価(危険!)

function safeEval(expr) {
  // 危険性のため実際の本番環境では推奨しない
  let f = new Function('return (' + expr + ')');
  return f();
}

console.log(safeEval('2 + 3 * 4')); // 14
JavaScript

注意expr"alert('hacked')" のような値だと実行されるので危険。

例 C:複数行でログ付きの関数

let logger = new Function(
  'name',
  'let t = Date.now(); console.log("start", name, t); return "done " + name;'
);
console.log(logger('Alice')); // start Alice 。。。 とログ、戻り値 "done Alice"
JavaScript

7. よくある質問(FAQ)

Q
new は必要?
A

必須ではない(Function('a','b','return a+b') でも OK)ですが、new を書く方が分かりやすいです。

Q
アロー関数は作れる?
A

Function コンストラクタで作れるのは通常の関数です。アロー関数(=>)の「レキシカル this」を再現することはできません。

Q
非同期(async)関数は?
A

普通の Function ではなく、AsyncFunction コンストラクタを使う方法もありますが(上級者向け)初心者はまず触れる必要はありません。


8. 演習問題 — 自分で手を動かしてみよう

問題1(基本)

new Function を使って、2 つの数の差を返す関数 sub を作り、sub(10, 4)6 を返すことを確認せよ。

解答例

let sub = new Function('a','b','return a - b');
console.log(sub(10,4)); // 6
JavaScript

問題2(スコープ確認)

次のコードの出力は何になるか予想せよ。

function test() {
  let local = 'hello';
  let f1 = function() { return local; };
  let f2 = new Function('return typeof local');
  console.log(f1());
  console.log(f2());
}
test();
JavaScript

答え

hello
undefined

f1 はローカル変数を参照できるが、f2 はできない)

問題3(注意喚起)

ユーザーが入力した式 expr をそのまま new Function に渡すと何が問題か、簡潔に説明せよ。
答え例:ユーザーが悪意のある JavaScript を入力すると、任意のコード(データの送信・削除など)が実行される可能性があるため危険。


まとめ(初心者が覚えておくこと)

  • Function コンストラクタは「文字列から関数を作る」ための特殊手段。使い方を知っておくと便利な場面もあるが、滅多に使わない
  • 普段は function 宣言・関数式・アロー関数を使おう。
  • Function はローカルスコープを捕捉できない(クロージャにならない)、セキュリティリスクがある、デバッグしにくい、という欠点を持つ。
  • どうしても動的生成が必要な場合は入力の検証とサニタイズを厳密に行い、代替手段がないか再検討すること。

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