Search code examples
javascriptperformancefunctionparsingfunction-constructor

JS Function-constructor re-parsed everytime?


In MDN, about Functions and function scope, what does it mean by parsed every time it is evaluated? Can this be observed by codes?

Quote in the section Function constructor vs. function declaration vs. function expression:

Functions defined by function expressions and function declarations are parsed only once, while those defined by the Function constructor are not. That is, the function body string passed to the Function constructor must be parsed every time it is evaluated. Although a function expression creates a closure every time, the function body is not reparsed, so function expressions are still faster than "new Function(...)". Therefore the Function constructor should be avoided whenever possible.

It should be noted, however, that function expressions and function declarations nested within the function generated by parsing a Function constructor 's string aren't parsed repeatedly. For example:

var foo = (new Function("var bar = \'FOO!\';\nreturn(function() {\n\talert(bar);\n});"))();
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.

I have written a code snippet to (try to) test and understand it:

var bar = 'FOO!';
var foo = (new Function("return(function() {\n\talert(bar);\n});"))();
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.

var bar2 = 'FOO!2';
var foo2 = function() { alert(bar2); };
bar2 = 'FOO!2 again';
foo2();

Both two alerts the "again-version".

What does it mean by reparsed or not?

Can this be illustrated by code results?

Thanks.


FYI, i have tried another code snippet:

var bar = 'FOO!';
var string1 = "return(function() {\n\talert(bar);\n});";
var foo = (new Function(string1))();
bar = 'FOO! again';
foo(); //The segment "function() {\n\talert(bar);\n}" of the function body string is not re-parsed.
string1 = "return(function() {\n\talert(bar + ' more');\n});";
foo();

Both alerts "FOO! again", not "FOO! again more".


Solution

  • What they wanted to highlight is that the JS parser needs to its work every single time the Function constructor is called - basically just the obvious. There is no caching of the passed code string involved.

    This is relevant [only] when compared with closures. Suppose we have these two functions:

    function makeAlerterParse(string) {
        return Function("alert("+JSON.stringify(string)+");");
    }
    function makeAlerterClosure(string) {
        return function alerter() { alert(string); };
    }
    

    Both function declarations will be parsed when the script is loaded - no surprises. However, in the closure also the alerter function expression is parsed already. Let's make some alerters:

    var alerter1 = makeAlerterParser("1"); // Here the parser will be invoked
    alerter1(); // no parsing, the function is instantiated already and 
    alerter1(); // can be interpreted again and again.
    
    var alerter2 = makeAlerterClosure("2"); // Here, no parser invocation -
    alerter2(); // it's just a closure whose code was already known
    alerter2(); // but that has now a special scope containing the "2" string
    

    Still no surprise? Good, then you have already understood everything. The warning is only that an explicit invocation like

    for (var fnarr=[], i=0; i<100; i++)
        fnarr[i] = makeAlerterParse(i);
    

    will really be 100 invocations of the JS parser, while the closure version comes for free.