Using The WITH Keyword With Javascript's Function() Constructor

Posted May 20, 2010 at 8:51 AM by Ben Nadel

Tags: Javascript / DHTML

Yesterday, I blogged about using the eval() function in conjunction with Javascript's Function() constructor in order to copy context variables into the local scope of the executing function. This was meant to allow non-scoped variables, references within the compiled function, to change with each execution. In a conversation following the post, Pedro del Gallego suggested that I might try looking into Javascript's WITH keyword as a way to accomplish the same outcome without eval() statements.

 
 
 
 
 
 
 
 
 
 

The "with" keyword is a black-sheep within the Javascript language. It's one of those constructs that you learn to frown upon because everyone else does, although you might not always know why. I never got into the "with" statement because I'm pretty fanatical about scoping my variables when a scope is available (with the exception of window-scoped values). But, it looks like the "with" statement might just be the perfect solution for this kind of problem.

When an unscoped (unqualified) variable is used in Javascript, the Javascript interpreter finds the value of that variable by walking up the scope chain available to the execution context. The "with" statement takes a given object and pushes it onto the current scope chain for the duration of the with-block. Once the block statement has finished executing, the given object is popped off and the scope chain returns to its previous state.

In my previous blog post, I used the eval() function to "extend" the local scope with individual context variables; in the following demo, however, I'm going to use the "with" keyword to actually add the "this" context onto the scope chain of the function execution:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>Using The WITH Keyword With Function Constructor</title>
  • <script type="text/javascript">
  •  
  • // I compile the given source code down into a Function
  • // that is executed in the context of the THIS object.
  • function FunctionProxy( sourceCode ){
  •  
  • // When executing the Function constructor, we are going
  • // to wrap the source code in a WITH keyword block that
  • // allows the THIS context to extend the local scope of
  • // the function.
  • //
  • // NOTE: This works without a nested self-executing
  • // function. I put it in there simply because it makes me
  • // feel a little more comfortable with the use of the
  • // WITH keyword.
  • return(
  • Function(
  • "with (this){" +
  • "return(" +
  • "(function(){" + sourceCode + "})()" +
  • ");" +
  • "};"
  • )
  • );
  • }
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • (function(){
  •  
  • // Create a function that makes references to variables
  • // that are not in its closure chain.
  • var sayHello = FunctionProxy(
  • "console.log( girlName + ', you in danger, girl!' );"
  • );
  •  
  •  
  • // Execute the function with a custom context. The WITH
  • // keyword in the function's proxied source code will
  • // be used to extend the local function scope with this
  • // custom context.
  • sayHello.apply({
  • girlName: "Molly"
  • });
  •  
  • })();
  •  
  • </script>
  • </head>
  • <body>
  • <!--- Intentionally left blank. --->
  • </body>
  • </html>

As you can see, I am taking the original source and wrapping it inside of a "with-block". I am then passing the "this" context reference to the "with" statement which will add it [this] to the function's scope chain. Then, as the function executes, any non-local, unqualified variables referenced within the source code will be searched for within the "this" scope.

Inside of the with-block, I am wrapping the original source code within a self-executing function block. This is not necessary and is something that I am doing purely for emotional reasons. By packaging the source code up in such a way, I feel like I am completely separating the execution of the source code from the scope-chain-extension afforded by the "with" statement. Again, not necessary, but emotionally satisfying.

When we run the above code, we get the following console output:

Molly, you in danger, girl!

As you can see, the context object applied to the sayHello() function by way of the apply() method was successfully used as one of the scopes in the unqualified-variable scope chain. Pretty awesome! I've never been a huge fan of the "with" keyword; but, a huge thanks to Pedro - this is a much more elegant solution than the one I had yesterday.




Reader Comments

May 20, 2010 at 10:04 AM // reply »
8 Comments

If you look into the interiors of some of the templating stuff out there (I believe I saw it in jQuery.tmpl and I know Underscore.js does it), they do something very similar with `with` to get the templating to work.

http://github.com/documentcloud/underscore/blob/master/underscore.js around line 627.


May 20, 2010 at 10:14 AM // reply »
10,638 Comments

@Brian,

Very cool - thanks for the tip. Looks like they are doing something very similar to what I was doing. I guess all the templating systems work very much the same.


Mar 4, 2011 at 12:41 AM // reply »
1 Comments

with is one of the cool keyword gives the awesome functionality and gives a lot means as well, i have not much idea about javascript but i have used with keyword in many languages.
anyway thanks for sharing this good information with us.
http://www.usedtrucksdeal.com/


Mar 4, 2011 at 8:01 PM // reply »
253 Comments

@Ben,

The first video at

http://www.smashingmagazine.com/2010/07/17/seven-must-see-videos-and-presentations-for-web-app-developers/

is Nicholas Zakas giving an awesome talk about speeding up JavaScript. At 5:21 into it, he begins to talk about "scope management". This is EXACTLY analogous to ColdFusion unscoped variable resolution.

As you know from Ben Forta's ColdFusion Certified Developer Exam Study Guide, there's an explicit sequence of scopes that CF uses to resolve an unscoped variable refernce. Well, turns out, JS does the same thing, except that the chain is variable length and based on execution context.

Start watching at 5:21, so that you get the meaning of his chain diagrams. Don't jump ahead to 9:39, where he begins talking about the effects of the with statement. Turns out that 'with (obj) { ... }' pushes a new context onto the front of the execution context stack. Between the braces, any name that matches a property of obj will be resolved to that property, and the scope resolution chain immediately terminates.

That's bad if you're trying to reference variables further down the chain, but it's good if you're mostly doing stuff obj's properties.

During the video, Nicholas Zakas references Douglas Crockford's "with Statement Considered Harmful" post in YUIBlog:

http://www.yuiblog.com/blog/2006/04/11/with-statement-considered-harmful/

In that post, Crockford cites indeterminence as the reason: "There is no way that you can tell by looking at the code which bing and bang will get modifed. Will [the object's properties] be modified? Or will the global variables bing and bang get clobbered? It is impossible to know for sure."

Well that may have been true at the time of that YUIBlog post on 04/11/2006 (Alessandra Ambrosio's birthday, by the way). But ECMA has eliminated that indeterminence. Now the problem is lengthening the scope resolution chain, if what you're trying to reference is outside the obj scope.

I guess what I'm trying to say is, Papa Crockford may consider 'with' harmful, but I don't. Like sex, it's not a "bad thing". It's just a "be careful thing". Bad things can happen if you're not careful.

Yeah, don't needlessly lengthen the scope resolution chain. But if you're very focused on an object's properties, using 'with' could be used to speed things up.

My $0.02.


Jul 11, 2011 at 1:33 AM // reply »
1 Comments

Augmenting the local scope of functions eluded me for months (with tons of searching) until tonight when I was re-reading John Resig's Micro-Templating post. http://ejohn.org/blog/javascript-micro-templating/

Specifically, his use of the

  • Function

constructor made me realize I could simply create a closure around some

  • var

statements. Once I had a working version and a much better understanding, my first search brought me here. The

  • with

statement makes total sense, I didn't realize it also augments the scope chain. I've only seen it used to extend objects.

Anyway, I created a jsperf test on the subject and using a

  • with

statement is the fastest. It would be nice not to have to use

  • apply

when calling the function, but trying to truly stringify an object in older browsers is just too messy.

Feel free to edit the test. http://jsperf.com/augmenting-the-scope-chain


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 3, 2012 at 10:49 PM
How I Got Node.js Running On A Linux Micro Instance Using Amazon EC2
Wow this was really helpful! Only thing I would add is you need to update your .bash_profile after you edit the secure_path. This is what I did: $ . ~/.bash_profile Otherwise, NPM won't be found. ... read »
Feb 3, 2012 at 10:14 PM
Pushing Base64-Encoded Images Over HTML5 WebSockets With Pusher And ColdFusion
@Ben, Just wanted to let you know that pusher are soon to start limiting sizes on messages. This was the detail that came through in the Feb dispatch: "However, we will soon be limiting the s ... read »
Feb 3, 2012 at 5:05 PM
Regular Expressions Make CSV Parsing In ColdFusion So Much Easier (And Faster)
I tried using your RegEx in my C# program, but it was matching an extra empty-string at the end and so I would end up with an extra field that doesn't exist, so I changed it to this: (^|,)("(?: ... read »
Feb 3, 2012 at 3:47 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
Josh Cyr posted this on Twitter just a little bit ago. Thought it was appropriate. http://stackoverflow.com/questions/1619152/how-to-create-rest-urls-without-verbs/1619677#1619677 ... read »
Feb 3, 2012 at 2:28 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
@Michael, You definitely make a good point (and extra points for quoting movies - I love movies). When you use a return() statement to define the object's public API, it does provide a consistent a ... read »
Feb 3, 2012 at 2:04 PM
Changing The Execution Context Of Your Self-Executing Function Blocks In JavaScript
To quote Jurassic Park: "Just because you can doesn't mean you should". I completely, utterly disagree with the thought that this is more readable. Consider the current module pattern: if ... read »
Feb 3, 2012 at 1:10 PM
REST API Design Rulebook By Mark Masse
@Jordan, Yeah, WRML was created by Mark Masse (author of the book). I also found it to be a bit convoluted. I suppose it is intended to allow the Client to be able to programmaticaly respond to cha ... read »
Feb 3, 2012 at 1:08 PM
ColdFusion Supports HTTP Verbs PUT And DELETE (As Well As GET And POST)
@Jason, To be honest, I don't have good answers for that kinds of stuff. And, to the point, that is specifically why I *really* liked the REST API Design Rulebook by Mark Masse - he just cuts throu ... read »