3

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".

midnite
  • 5,157
  • 7
  • 38
  • 52
  • Evaluating the variable `bar` is not the same as parsing the expression. – Barmar Dec 20 '13 at 03:07
  • 1
    I think the distinctions being made only affect performance, not the effect of the code. – Barmar Dec 20 '13 at 03:08
  • Thanks @Barmar, i also think this is mainly a performance issue. But, to my understanding, re-parsing which is like re-reading (or re-compiling) the code. So if that piece of code is altered between each time of parsing, in some cases it should be able to reflected in the results. – midnite Dec 20 '13 at 03:14
  • Of course. But then it's a different function constructor. – Barmar Dec 20 '13 at 03:16
  • 1
    What they're saying is that even if the parameters passed to the `Function` constructor are the same strings as previously, they will be re-parsed. – Barmar Dec 20 '13 at 03:17

2 Answers2

1

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thanks @Bergi. Do you mean in the "closure version", the `function alerter()` is already pre-processed (pre-parsed), which is like that function is already defined (and compiled) at the top of the scope? But the Function constructor will not (likely to) be moved to the top, and which is like an ordinary statement, that needs to be run everytime we come across it. If you can include an example, with different visible results, it would be very nice! – midnite Dec 20 '13 at 04:23
  • Yes, the closure does not need any extra parse step - it can be compiled when the script is read in. Executing `makeAlerterClosure` will just create function objects with different scope contents that do however share the (once) compiled code. Indeed, the Function constructor is an ordinary expression, and we need to run the parser every time we come across it. No, I cannot include an example of different behaviour, since they are functionally equivalent. The `Function` constructor is just big-time inefficient, and should never be used for this. – Bergi Dec 20 '13 at 04:40
0

My understanding is that with a function constructor, the engine stores the body string as a string rather than as the function it creates; thus, it would need to be reparsed (converted from a string to a function) every time you use it.

Whereas a function declaration or expression parses it the first time, and stores it in memory as a function, so whenever you use it, it goes to the memory location of the function to access it.

If we look at your example, I think it could be read like this:

var bar = 'FOO!';
var foo = (new Function("return(function() {\n\talert(bar);\n});"))();
// function() {\n\talert(bar);\n} is a function declaration, so when it's evaluated
// the first time, the engine pulls out the function and stores it as an anonymous function

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

'FOO! again' is the expected output since the function just references the variable bar, so once the foo is constructed, it just points to the variable as opposed to taking its value.

I think foo would be stored something like this:

"return function_location"

Which would get parsed every time it's executed.

In your last example, it doesn't alert 'FOO! again more' because when you used the constructor, it saved it as a string rather than a pointer to the variable. But what's interesting about your last example is that it stores the outer variable as a string, yet keeps the inner variable as it is.

EmptyArsenal
  • 7,314
  • 4
  • 33
  • 56
  • Thanks for answer, and tried to explain the details. +1 for `"return function_location"`. And yes, it is interesting in the last example that the Function constructor actually **copies** the string. – midnite Dec 20 '13 at 03:58
  • I think it's a tough one to really understand and explain. You need the ear of someone from the V8 or SpiderMonkey team... – EmptyArsenal Dec 20 '13 at 04:00
  • No, it is really not parsed every time it is used. The parsing might be delayed until the first call (as an optimisation), but there is only one parser run per `Function()` call. – Bergi Dec 20 '13 at 04:08
  • @Bergi: Do you have a reference for that the function is only parsed once? – Marc Jan 09 '14 at 19:19
  • @Marc: Not yet, you could look it up in the source code of the js engine. It wouldn't make a difference to the user; why would it be parsed multiple times if only once is needed? – Bergi Jan 09 '14 at 19:31
  • I don't know either why it should be parsed multiple times. However, otherwise this quote from above: `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.` together with what follows doesn't make much sense. – Marc Jan 09 '14 at 20:21