Javascript Function() Constructor Does Not Create A Closure

Posted April 28, 2010 at 8:38 AM by Ben Nadel

Tags: Javascript / DHTML

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.




Reader Comments

Apr 28, 2010 at 8:54 AM // reply »
8 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.


Apr 28, 2010 at 8:57 AM // reply »
8 Comments

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


Apr 28, 2010 at 8:58 AM // reply »
10,640 Comments

@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 :(


Apr 28, 2010 at 8:59 AM // reply »
10,640 Comments

@Brian,

Ha ha, got ourselves a little comment-tag going on :)


Apr 28, 2010 at 9:05 AM // reply »
3 Comments

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.


Apr 28, 2010 at 9:15 AM // reply »
10,640 Comments

@Kathryn,

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


Apr 28, 2010 at 11:50 AM // reply »
8 Comments

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


Apr 28, 2010 at 12:13 PM // reply »
10,640 Comments

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


May 5, 2010 at 5:36 AM // reply »
1 Comments

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


May 5, 2010 at 9:49 AM // reply »
1 Comments

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


May 6, 2010 at 3:56 AM // reply »
1 Comments

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.


May 6, 2010 at 9:41 PM // reply »
10,640 Comments

@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 :)


May 6, 2010 at 9:48 PM // reply »
8 Comments

@Ben,

You can eval huge chunks of logic.

http://blog.sproutcore.com/post/225219087/faster-loading-through-eval

I just started listening to The Dev Show on 5by5.tv ( http://5by5.tv/devshow ) and they talked about that link above on their first show.

Surprising results, to say the least. Check it out. :)


May 6, 2010 at 9:54 PM // reply »
10,640 Comments

@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 :)



Post A Comment

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.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
Feb 8, 2012 at 11:09 AM
Building A Fixed-Position Bottom Menu Bar (ala FaceBook)
Here's a warning about using fixed top or bottom menu bars that hasn't been mentioned. Browsers don't factor fixed bars into the page height for page up and page down, meaning you'll have anything f ... read »
Feb 8, 2012 at 10:32 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
@Andy, Ah, very cool. FW/1 really seems to be quite well-rounded these days! ... read »
Feb 8, 2012 at 9:52 AM
Building A Twitter-Inspired RESTful API Architecture In ColdFusion
Just wanted to let you know that version 2.0 of Sean Corfield's FW/1 supports routing. This allows you to build true RESTful APIs using ColdFusion. (search for "URL Routes" https://github ... read »
Feb 8, 2012 at 2:05 AM
Creating A Fixed-Length Queue In JavaScript Using Arrays
Cool site ... read »
Feb 7, 2012 at 5:00 PM
Ask Ben: Ending ColdFusion Session When User Closes Browser
We've used code that sets the cookies without the "expires" attribute in most of our applications to accommodate an "automatic logoff" (Let's face it, that's what we're really try ... read »
Feb 7, 2012 at 10:10 AM
Ask Ben: Building An AJAX, jQuery, And ColdFusion Powered Application
Hey Ben great post. Just like @Carl Steinhilber I am having the same trouble with the -'parseerror'. But I can only reach GET contacts-demo.cfm I have added secureJSON="no" ... read »
ang
Feb 7, 2012 at 4:46 AM
Using The Apple iPod Shuffle Without iTunes
i got the same error with munkey!! help please!! ... read »
Feb 7, 2012 at 2:48 AM
Ask Ben: Javascript String Replace Method
@Kadut, . is a special character you will need to escape it \. ... read »