The "new Function" syntax

You already know several ways to make a function: declarations, expressions, arrow functions. Here’s one more. You’ll reach for it rarely, but a handful of problems have no clean alternative, and it’s worth understanding what makes it different from everything else.

What makes this one unusual is that the function’s code lives in a string. Everything else you write gets parsed when the file loads. This function gets built while your program is already running.

Syntax

The constructor looks like this:

let func = new Function ([arg1, arg2, ...argN], functionBody);

You hand it the parameter names first, then the body as the final argument. Every argument is a string. The last string is the code that runs when you call the function; the ones before it are the names of the parameters.

An example makes the shape obvious. A function that takes a width and a height and returns the area:

let area = new Function('w', 'h', 'return w * h');

alert( area(3, 4) ); // 12
new Function(‘w’, ‘h’, ‘return w * h’)
↑ param w↑ param h↑ the body (always last)
// equivalent to writing:
function (w, h) { return w * h; }
The last string is the body. Everything before it names a parameter.

If your function needs no parameters, pass only the body:

let ping = new Function('alert("pong")');

ping(); // pong

The important difference from every other way you’ve created a function: the body is a string, supplied at run time. Every declaration and expression you’ve written before this had to sit in your source code, typed out ahead of time and parsed when the script loaded.

new Function lets you turn any string into a live function. That string can come from anywhere — including places your source file never saw:

let str = ... receive the code from a server dynamically ...

let func = new Function(str);
func();

Try it yourself. The box below holds a string of code. Nothing turns it into a function until you press the button — that’s new Function building a working function while this page is already running.

interactiveBuild a function from a string at run time

Closure: why its scope is different

Here’s where new Function breaks from the pattern in a way that surprises people.

Normally a function remembers the place it was born. It keeps that memory in an internal property called [[Environment]], which points at the Lexical Environment where the function was created. That reference is what makes closures work — it’s how an inner function still sees the variables of the outer function that made it. We went through the mechanics in Variable scope, closure.

A function built with new Function is different. Its [[Environment]] is wired to the global Lexical Environment, not the one it was created inside.

regular function() {}
Global
makeGreeter { phrase }

show → [[Environment]]

↑ points up to makeGreeter’s scope
new Function(…)
Global
makeGreeter { phrase }

show → [[Environment]]

↑ skips makeGreeter, points to Global
A regular function's [[Environment]] points to where it was born. A new Function's points to global — always.

The practical consequence: such a function can’t reach outer variables. It only sees globals.

function makeGreeter() {
  let phrase = "welcome";

  let show = new Function('alert(phrase)');

  return show;
}

makeGreeter()(); // error: phrase is not defined

Compare that with a normal function expression, which closes over phrase just fine:

function makeGreeter() {
  let phrase = "welcome";

  let show = function() { alert(phrase); };

  return show;
}

makeGreeter()(); // "welcome", from the Lexical Environment of makeGreeter

Same structure, different outcome. The regular function on line 4 remembers makeGreeter’s scope and finds phrase on line 9. The new Function version looks for phrase in the global scope, doesn’t find it, and throws.

See both halves side by side. One function tries to read a global variable; the other tries to read a local one born inside the function that created it. Only the global lookup succeeds.

interactiveGlobal: yes. Outer local: no.

This looks like an arbitrary limitation. It isn’t. It solves a real problem.

Why global scope is the right call

Picture the scenario new Function exists for. You need a function whose code you don’t know while writing the script — that’s the whole reason you’re not writing a normal function. The code shows up later: from a server, a template, some other source. And that new function has to cooperate with your main script somehow.

So a tempting wish: what if the generated function could just reach into the outer variables around it?

Here’s what wrecks that wish. Before JavaScript ships to production, it’s run through a minifier — a tool that shrinks the code by stripping comments and whitespace and, crucially, renaming local variables to shorter names. A let userName becomes let a (or whatever short name is free), and every reference to it inside the function is renamed to match.

// you wrote
let userName = “Raj”;
alert(userName);
// minifier ships
let a=“Raj”;
alert(a);
A run-time string like ‘alert(userName)’ still says userName
but the variable is now called a. Nothing to find.
A minifier renames locals everywhere it can see them. A run-time string is invisible to it.

Renaming locals is normally safe. A local variable is invisible from outside the function, so nothing external depends on its name. The minifier analyzes the code’s structure and renames every reference in lockstep — it’s careful, not a blind find-and-replace, so ordinary code keeps working.

Now add new Function with access to outer variables. The generated function’s body is a string. The minifier can’t look inside a string it will only build at run time, so it won’t rename the mention there. Your string still says userName; the actual variable is now called a. The lookup fails.

So how do you get data into one of these functions? Through its parameters. Pass values in explicitly as arguments — that channel is stable, minifier-proof, and easy to follow.

// pass data in through arguments, not through outer scope
let area = new Function('w', 'h', 'return w * h');

let width = 3, height = 4;
alert( area(width, height) ); // 12 — width and height go in as arguments

The demo below builds area exactly once with new Function, then feeds it values through its parameters. Change the numbers and the same generated function keeps working — no outer scope involved.

interactiveData goes in through parameters

Summary

The syntax:

let func = new Function ([arg1, arg2, ...argN], functionBody);

For historical reasons, the parameter names don’t have to be separate arguments — you can also pack them into one comma-separated string. All three of these produce the same function:

new Function('w', 'h', 'return w * h'); // basic syntax
new Function('w,h', 'return w * h');    // comma-separated
new Function('w , h', 'return w * h');  // comma-separated with spaces
‘w’, ‘h’, ‘return w * h’
‘w,h’, ‘return w * h’
‘w , h’, ‘return w * h’
→ all build: function (w, h) { return w * h; }
Three ways to spell the parameters; one identical function.

What sets these functions apart is scope. Their [[Environment]] references the global Lexical Environment rather than the place they were created, so they can’t touch outer variables. That’s a feature, not a bug: it keeps them working after minification and steers you toward passing data in as arguments — clearer to read and far less error-prone than reaching into surrounding scope.