Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the New York ColdFusion User Group (Jan. 2009) with:

Javascript Function() Constructor Does Not Create A Closure

By Ben Nadel on

Yesterday, when I was building the basis of the jQuery Template Markup Language (JTML), I made use of Javascript's Function() constructor to dynamically generate HTML template rendering engines. I've only use the Function() constructor once or twice before so I'm not too familiar with it. I know that ordinarily, when you define a function, it creates a closure with its parent scope; but I wasn't sure what kind of bindings are generated when a function is crated with a Function() constructor call.

 
 
 
 
 
 
 
 
 
 

Testing this was rather easy - all I had to do was create two like-named variables in competing scopes. Then, I had to use the Function() constructor to define a function within the inner scope that makes reference to said variable. If the resultant function references the inner variable, a closure was created; if not - if the function references the outer variable - no closure was created.

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>Javascript Function() Context</title>
  • <script type="text/javascript">
  •  
  • // Define this variable in the Window scope.
  • var scope = "window";
  •  
  •  
  • // Run a self-contained function.
  • (function(){
  •  
  • // Create a locally scoped variable.
  • var scope = "function";
  •  
  • // Define a function that logs the current scope value.
  • // If this creates a closure, it will log, "function."
  • // If it does not, it will log, "window."
  • var testScope = new Function(
  • "console.log( scope );"
  • );
  •  
  • // Execute the test function.
  • testScope();
  •  
  • })();
  •  
  • </script>
  • </head>
  • <body>
  • <!-- Left blank intentionally. -->
  • </body>
  • </html>

As you can see here, I have dually defined the "scope" variable in the window context as well as in the self-contained function. The function, testScope(), is then created within the self-contained function and does nothing more than log the "closest" scope value. When I run this page, I get the following console output:

window

As you can see, the generated function found the window-scoped variable which means that its creation did not form a closure to the self-contained function. While I understand why this might happen, I think it would have been awesome if the Function() constructor created a closure to the calling context. I just think this would have been much more useful, and, in a way, might even be more consistent with how all other function declarations work in Javascript.



Looking For A New Job?

100% of job board revenue is donated to Kiva. Loans that change livesFind out more »

Reader Comments

I'm not sure if I've really seen functions created like that. If you tweak the interior to something like:

var testScope = function() {
console.log( scope );
};

Then it outputs 'function' instead of 'window'.

Definitely seems like something about going the new Function route breaks the inherited scope, which is odd but could be an interesting trick.

Reply to this Comment

Posted a bit prematurely: I realize you're aware that the non-quoted syntax works fine, but guess there's likely a reason for trying to use the new Function syntax that I'm not aware of. :D

Reply to this Comment

@Brian,

Yeah, most definitely. If you watch the video, I actually do just what you are saying to demonstrate the difference in lexical binding between the two function declarations. It just would have been cool :(

Reply to this Comment

I believe the new operator takes the scope from where the object base is located, in this case the object base is "Function" and as such it is located within the window scope. ie typeof window.Function returns "function" which is why when you use function with the new operator it gives you the "scope" variable from the window scope.

Reply to this Comment

@Kathryn,

Yeah, probably. Most of me expected this behavior; but, part of me hoped it didn't work that way.

Reply to this Comment

@Ben,

I should have watched the video. I was waking up and trying to be quiet or might have otherwise. :D

Also, I decided to go reading, and on page 117 of the ECMAScript language spec I dug up (well, page 127 of the PDF but it's numbered 117), it explicitly says that when using the function constructor like this, that it receives the global scope. Seems like it'd be really nice if there were a way to use this sort of syntax but then specify to act as though it were a standard function declaration.

Reply to this Comment

@Brian,

I thought you might be able to use call() or apply(), but as far as I know, that only works for "this". It doesn't actually affect the implicit scope chain.

Reply to this Comment

In my opinion if you are using new Function your missing the power of closures. However if you really need this try function(){eval("...")}

var foo = "win";
(function(){
var foo = "fun";
(function(){eval("console.log('eval: ' + foo)")})();
(new Function("console.log('Func: ' + foo)"))();
(function(){console.log('func: ' + foo)})();
})();

gives

eval: fun
Func: win
func: fun

Reply to this Comment

<q>I believe the new operator takes the scope from where the object base is located, in this case the object base is "Function" and as such it is located within the window scope. ie typeof window.Function returns "function" which is why when you use function with the new operator it gives you the "scope" variable from the window scope.</q>

By the same logic, eval should work as Function, but it does not.

Presumably the reason for the difference of scope resolution of eval and Function is simply due to the spec. The 262 version denotes (in section 10.2.2) that eval runs in the same context as the calling context (the SAME context, no new activation record is pushed on the stack)

(function(){
var foo = "fun";
eval("foo = 'eval';");
alert(foo); // shows eval
})();

Where as Function, when called as a function works differently (sec 15.3.2.1 step 16).

I'd just guess that it's either:

1. A botched job from having to create a standard from a complex language (possibly backwards compatability issues)
2. A "nice" way to be able to setup two different scope chains, depending on what you need. Your way of using Function above is not any different if you used eval, which would get you what you want.

Reply to this Comment

Your function creation still creates a closure.
Right before the call to testScope(), assign a different value to the scope variable. It will still print "window" to the console.

The problem is just that the creation of the new Function uses the global variable instead of the local variable. I'm not sure why that is, but at least it still encapsulates state, which results in a closure.

Reply to this Comment

@Eval,

I am not a huge fan of eval(). I like it for things like evaluation JSON (although I know people see that as faux pas these days. I am not sure that I can really get what I want out of closures either. I LOVE LOVE LOVE closures, but the problem is that I need to locally scope some variables in the calling scope for use in the compiled function and creating a closure in the traditional sense I am not sure would have solved the problem.

@Svend,

I wonder if I can stuff more things into eval() than I realized. I know you can eval a single line of Javascript; but, I wonder how well it holds up trying to eval() several lines of Javascript logic. Perhaps I'll play with that as an experiment.

@Ampersandre,

Right - I guess the problem is not so much that a closure wasn't created... more that it wasn't the closure I wanted :)

Reply to this Comment

@Brian,

Awesome my man. I'll be checking that out first thing in the morning. Right now, however, I need to create a solid combination of PB&J sandwiches and TV :)

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.