43

As repeatedly said, it is considered bad practice to use the Function constructor (also see the ECMAScript Language Specification, 5th edition, § 15.3.2.1):

new Function ([arg1[, arg2[, … argN]],] functionBody)

(where all arguments are strings containing argument names and the last (or only) string contains the function body).

To recapitulate, it is said to be slow, as explained by the Opera team:

Each time […] the Function constructor is called on a string representing source code, the script engine must start the machinery that converts the source code to executable code. This is usually expensive for performance – easily a hundred times more expensive than a simple function call, for example. (Mark ‘Tarquin’ Wilton-Jones)

Though it's not that bad, according to this post on MDC (I didn't test this myself using the current version of Firefox, though).

Crockford adds that

[t]he quoting conventions of the language make it very difficult to correctly express a function body as a string. In the string form, early error checking cannot be done. […] And it is wasteful of memory because each function requires its own independent implementation.

Another difference is that

a function defined by a Function constructor does not inherit any scope other than the global scope (which all functions inherit). (MDC)

Apart from this, you have to be attentive to avoid injection of malicious code, when you create a new Function using dynamic contents.

That said, T.J. Crowder says in an answer that

[t]here's almost never any need for the similar […] new Function(...), either, again except for some advanced edge cases.

So, now I am wondering: what are these “advanced edge cases”? Are there legitimate uses of the Function constructor?

Community
  • 1
  • 1
Marcel Korpel
  • 21,536
  • 6
  • 60
  • 80
  • ES5 in strict mode doesn't throw when `new Function` is used... – Šime Vidas Dec 09 '11 at 22:42
  • @Šime: According to the spec (Annex C), it should: “Attempting to dynamically define such a strict mode function using the Function constructor (15.3.2) will throw a `SyntaxError` exception”, but a test revealed it doesn't. Am I misunderstanding something? – Marcel Korpel Dec 10 '11 at 17:46
  • Well, read the preceding sentence. "Such a strict mode function" refers to a function which either name or one of the parameters is either "eval" or "arguments". – Šime Vidas Dec 10 '11 at 17:54

7 Answers7

19

NWMatcher — Javascript CSS selector and matcher, by Diego Perini — uses Function constructor (1, 2, 3, 4, etc.) to create ("compile") highly-efficient versions of selector matchers.

The benchmark (which I just ran on Chrome 5) speaks for itself:

alt text

Note the difference between NWMatcher and Sizzle, which is a very similar selector engine, only without function compilation :)

On a side note, ECMAScript 5 doesn't throw any errors on invocation of Function. Neither in strict, nor in "standard" modes. Strict mode, however, introduces few restrictions on presence of identifiers such as "eval" and "arguments":

  • You can't have declare variables/functions/arguments with such names:

    function eval() { }
    var eval = { };
    function f(eval) { } 
    var o = { set f(eval){ } };
    
  • You can't assign to such identifier:

    eval = { };
    

Also note that in strict mode, eval semantics is slightly different from that in ES3. Strict mode code can not instantiate variables or functions in the environment from which it was called:

 eval(' "use strict"; var x = 1; ');
 typeof x; // "undefined"
Community
  • 1
  • 1
kangax
  • 38,898
  • 13
  • 99
  • 135
  • Ext.js' selector engine (http://code.google.com/p/extjs-public/source/browse/trunk/release/source/core/DomQuery.js) has used function compilation for ages also (see `compile`) – Crescent Fresh Jun 12 '10 at 06:39
  • 2
    I've updated the source code references. But the benchmark has become a dead link. – gblazex Oct 29 '10 at 21:34
  • [Unless I’m missing something](http://mathiasbynens.be/notes/javascript-properties), ES5 strict mode allows both `var o = { eval: 1 }` and `myObject.eval = 1;`. Your other examples check out, though. Did the spec change since you wrote this answer? – Mathias Bynens Apr 24 '12 at 07:08
  • 1
    @MathiasBynens Looks like I misinterpreted 11.1.5 which says _"It is a SyntaxError if the Identifier "eval" or the Identifier "arguments" occurs as the Identifier in a PropertySetParameterList of a PropertyAssignment that is contained in strict code or if its FunctionBody is strict code."_ (http://es5.github.com/#x11.1.5) `set f(eval) { }` is what throws error, not `{ eval: ... }`. Thanks for catching that! I updated the answer. – kangax May 01 '12 at 19:00
9

I use the new Function() constructor as an in-line JS interpreter in one of the web apps I'm developing:

function interpret(s) {
  //eval(s); <-- even worse practice
  try {
      var f = new Function(s);
      f();
    }
  catch (err) {
      //graceful error handling in the case of malformed code
  }
}

As I get stuff streaming over AJAX (not an iframe), I continuously interpret() it on readyStateChange == 3. This works surprisingly well.

Edit: here's a clear case study that shows that new Function() is categorically faster than eval(). I.e. you should never (rarely?) use eval in lieu of new Function().

http://polyfx.com/stuff/bsort.html <- the 1000 iteration version, may crash your browser

http://polyfx.com/stuff/bsort10.html <- the shorter version

Eval is on average, almost 8 times slower than new Function().

David Titarenco
  • 32,662
  • 13
  • 66
  • 111
  • 5
    What exactly is "worse practice" about evaluating string of code via `eval` comparing to doing it via `Function` constructor? – kangax Jun 11 '10 at 21:29
  • 2
    It runs faster and it's easier to understand: http://www.go4expert.com/forums/showthread.php?t=13979 Also, it's easier to drop `f()` in a specific scope or closure if need be (for security reasons). – David Titarenco Jun 11 '10 at 22:21
  • 2
    Edited my answer with a case study I just wrote. Eval is in some browsers almost 10 times slower. – David Titarenco Jun 11 '10 at 22:59
8

jQuery uses it to parse JSON strings when a JSON parser object is not available. Seems legit to me :)

        // Try to use the native JSON parser first
        return window.JSON && window.JSON.parse ?
            window.JSON.parse( data ) :
            (new Function("return " + data))();
harpo
  • 41,820
  • 13
  • 96
  • 131
  • 1
    Using the full interpreter to parse a data format isn't ideal; it opens the door to script injection. (*Lots* of people do it, though, it's not just jQuery. :) ) – T.J. Crowder Jun 11 '10 at 21:45
  • 1
    The data is already sanitized at this point by jQuery, this is done purely for speed. – Brendan Heywood Jul 31 '10 at 15:07
7

John Resig used the Function constructor to create "compiled" versions of client-side templates written in an asp syntax. http://ejohn.org/blog/javascript-micro-templating/

Prestaul
  • 83,552
  • 10
  • 84
  • 84
  • John Resig's implementation inspired underscore's micro-templating language, which uses the same approach (i.e., the `Function` constructor): http://underscorejs.org/docs/underscore.html#section-143 – turtlemonvh Apr 01 '14 at 21:04
4

This is a separate case from my other answer.

I used the Function constructor a while back to create custom string formatters that were being called repeatedly. The overhead of creating the function (which I take it is the performance issue you're talking about) was far outweighed by the improved performance of the custom-built functions, which were created at runtime specifically to process a particular format string, and therefore did not need to evaluate tons of irrelevant cases — or parse a format string, for that matter. It's a bit like compiling a regular expression, I suppose.

harpo
  • 41,820
  • 13
  • 96
  • 131
3

The only legitimate use I have come for it is when I wrote this:

Function.prototype.New = (function () {
  var fs = [];
  return function () {
    var f = fs [arguments.length];
    if (f) {
      return f.apply (this, arguments);
    }
    var argStrs = [];
    for (var i = 0; i < arguments.length; ++i) {
      argStrs.push ("a[" + i + "]");
    }
    f = new Function ("var a=arguments;return new this(" + argStrs.join () + ");");
    if (arguments.length < 100) {
      fs [arguments.length] = f;
    }
    return f.apply (this, arguments);
  };
}) ();

The code allows you to use Function.prototype.apply while 'using' the new keyword.

Example:

function Foo (x, y, z) {
  this.x = x;
  this.y = y;
  this.z = z;
  this.otherArgs = Array.prototype.slice.call (arguments, 3);
}

var foo = Function.prototype.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]);
// /*equiv*/ var foo = Foo.New.apply (Foo, [1, 2, 3, 4, 5, 6, 7]);
// /*equiv*/ var foo = Foo.New (1, 2, 3, 4, 5, 6, 7);
var bool = true
  && foo.x == 1
  && foo.y == 2
  && foo.z == 3
  && foo.otherArgs.length == 4
  && foo.otherArgs [0] == 4
  && foo.otherArgs [1] == 5
  && foo.otherArgs [2] == 6
  && foo.otherArgs [3] == 7
  ;

alert (bool);
Thomas Eding
  • 35,312
  • 13
  • 75
  • 106
  • 1
    This use case can now be accomplished using `Object.create` in ES5-compliant browsers: `var newObj = Object.create(MyClass.prototype); var ret = MyClass.apply(newObj, constructorArgs); if (ret && typeof ret === "object") { newObj = ret; }` – kpozin Apr 17 '12 at 04:37
2

You might want to execute a string of code more than once. Using the Function constructor means that you only have to compile it once.

You might want to pass arguments to the code, for instance if you're polyfilling an event you can retrieve the event attribute and construct a Function expecting an event argument.

You can combine the two and compile it in one location and execute it at another and still manage to pass arguments in the variables that the string of code expects.

Neil
  • 54,642
  • 8
  • 60
  • 72