1

From the MDN description of Function:

Note: Functions created with the Function constructor do not create closures to their creation contexts; they always are created in the global scope. When running them, they will only be able to access their own local variables and global ones, not the ones from the scope in which the Function constructor was called. This is different from using eval with code for a function expression.

I understand,

            var y = 10;
            var tester;
            function test(){
                var x = 5;

                tester = new Function("a", "b", "alert(y);");
                tester(5, 10);
            }
            test();           // alerts 10

Replacing the tester = new Function("a", "b", "alert(y);"); with tester = new Function("a", "b", "alert(x);");, I will get

// ReferenceError: x is not defined

But couldn't understand the author's line-

...they always are created in the global scope.

I mean how is the new Function("a", "b", "alert(y);"); nested within the test fn is in global scope?

In fact, accessing it from outside the test fn will simply result in

Uncought TypeError:tester is not a function

Please elucidate.

Farhan stands with Palestine
  • 13,890
  • 13
  • 58
  • 105
  • 1
    You are right.The docs have worded it wrong. In fact they have corrected the wording now. `However, unlike eval, the Function constructor creates functions which ***execute*** in the global scope only.` (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function) – Number945 Feb 03 '20 at 20:14

4 Answers4

2

In your example, "created in the global scope" means that tester will not have closure over x from test:

function test(){
  var x = 5;
  tester = new Function("a", "b", "alert(x);"); // this will not show x
  tester(5, 10);
}

When you new up a Function, it does not automatically capture the current scope like declaring one would. If you were to simply declare and return a function, it will have closure:

function test(){
  var x = 5;
  tester = function (a, b) {
    alert(x); // this will show x
  };
  tester(5, 10);
}

This is the trade-off you make for having dynamically compiled functions. You can have closure if you write the function in ahead of time or you can have a dynamic body but lose closure over the surrounding scope(s).

This caveat doesn't usually matter, but consider the (slightly contrived) case where you build a function body as a string, then pass it to a function constructor to actually be evaluated:

function addOne(x) {
  return compile("return " + x + " + 1");
}

function addTwo(x) {
  return compile("return " + x + " + 2");
}

function compile(str) {
  return new Function(str);
}

Because the function is instantiated by compile, any closure would grab str rather than x. Since compile does not close over any other function, things get a bit weird and the function returned by compile will always hold a closure-reference to str (which could be awful for garbage collection).

Instead, to simplify all of this, the spec just makes a blanket rule that new Function does not have any closure.

ssube
  • 47,010
  • 7
  • 103
  • 140
  • A little clarification. You said, ..compile will always hold a closure-reference to str. That means new Function(...) in the above case is a closure that becomes available outside of the enclosing function and thus must retain a variable state(in this case str) to act in a meaningful way. But on the other line, you said-a blanket rule that new Function does not have any closure (OPPOSITE to what you said before). Could you elaborate a bit more. Thanks. – Farhan stands with Palestine Jun 07 '16 at 09:03
  • I poorly worded the second bit. If new functions were to get closure, the second form would close over str rather than x as you may expect. In reality, it will close over neither. – ssube Jun 07 '16 at 12:32
0

You have to create an object to expose via return inside the test() function for it to be global. In other words, add var pub = {} and name your internal functions as properties and/or methods of pub (for example pub.tester = new func) then just before closing test() say return pub. So, that way it will be publically available (as test.tester). It's Called the Revealing Module Pattern.

Ronnie Royston
  • 16,778
  • 6
  • 77
  • 91
  • This is a rather different thing. Dynamically-created functions (using `eval` or `new Function`) behave quite differently from normal declared functions. – ssube Jun 06 '16 at 16:37
0

What it means is that inside the function you can only refer to global variables, as you've found. However, the reference to the function itself is still in the local scope where it was created.

gcampbell
  • 1,252
  • 15
  • 13
-1

I'm confused as to where the confusion is.

It says that the function will be in global scope...and therefore will only have access to its own scope and the global scope, not variables local to the scope in which it was created.

You tested it and it has access to its own scope and the global scope, not variables local to the scope in which it was created.

So where's the confusion?

Is it in your assigning of the function to the variable testing? testing is just a local variable with a reference to the function...that has nothing to do with the scope of the creation of the function.

Scope is lexical, and has to do with where the function is created, not what random variables a function reference happens to be assigned to at runtime. And the documentation is telling you that when you make a function this way it acts as if it was created in the global scope...so it's acting completely as expected.

Here's an illustration:

This:

var y = 10;
var tester;

function test()
{
    var x = 5;

    // 10 and errors as not defined
    tester = new Function("console.log(y); console.log(x);");
}

Is similar to this:

var y = 10;
var tester;

function test()
{
    var x = 5;

    // also 10 and errors as not defined
    tester = something;
}

function something()
{
    console.log(y);
    console.log(x);
}

NOT

var y = 10;
var tester;

function test()
{
    var x = 5;

    // 10 and 5...since x is local to the function creation
    tester = function()
    {
        console.log(y);
        console.log(x);
    }
}
BryanGrezeszak
  • 577
  • 2
  • 8