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 »
11,246 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 »
11,246 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 »
11,246 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 »
11,246 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 »
11,246 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 »
11,246 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
Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 23, 2013 at 9:52 PM
Preventing Links In Standalone iPhone Applications From Opening In Mobile Safari
@Muhmmadibn Did you figure out a solution to launching PDFs? I am running into the same issues myself. There is no way to close the PDF or go back once you launch it. Thanks in advance! ... read »
May 23, 2013 at 6:06 PM
The Girl Who Broke My Heart, And Made Me A Better Person
Good day,ladies and gentle men, my name is Dr AMADI the great spell caster in Africa, i have help so many people for different kind of problems,who say there is no solution to problems on earth, that ... read »
May 23, 2013 at 4:26 PM
ColdFusion QueryAppend( qOne, qTwo )
@Heather, Glad people are still getting value out of this! ... read »
May 23, 2013 at 3:49 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@WebManWalking, I meant the code at the bottom (not the video). I did try to experiment with an intermediary variable, like: value = users.id[ i ]; arrayContains( userIDs, value ); ... but t ... read »
May 23, 2013 at 11:06 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, Are you talking about As Number: YES As String: YES As Java: YES? If so, that's with 3 different ways of referencing the constant 1, not users.id[1]. Query object references(*) are what seem ... read »
May 23, 2013 at 9:55 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dan, According to the CF Admin, I'm running Java "1.6.0_45". As far as the DB column, in the database it's an INT. I'll see if I can dig into what CF sees it as. @WebManWalking, But h ... read »
May 23, 2013 at 9:49 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben, I think the problem is that we're used to loose typing in ColdFusion, like JavaScript. If a value is a number but it's needed in an expression to be a string, noooo problem. I've encountered ... read »
May 23, 2013 at 9:47 AM
ColdFusion QueryAppend( qOne, qTwo )
You rock! Thank you, thank you, thank you!!! ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools