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 jQuery Conference 2010 (Boston, MA) with:

Faking Context In Javascript's Function() Constructor

By Ben Nadel on

In my jQuery Template Markup Language (JTML) project, I needed a way to compile JTML templates down into Javascript functions such that they could be executed at any time in order to generate new HTML markup. This is a tricky problem because the JTML code makes references to non-scoped variables that have to be available in the rendering engine's context at the time that it executes. When I coded this project, the only solution that I could figure out at the time was to actually create a new Function() every time a template needed to be rendered. This, to some degree, defeats the purpose of compiling the rendering engine ahead of time. After thinking about this problem for a while, I wondered if I could leverage Javascript's apply() functionality to create dynamically-scoped, pre-compiled functions.

 
 
 
 
 
 
 
 
 
 

When you use the apply() or call() methods to change the execution context of a given function, all it really does is change the "this" reference; this has no effect on non-scoped variables, which will still be found by crawling up the function's closure chain. However, what if we had a function that checked for its own "this" context before it executed? If we created a function that appended this-scoped variables onto its local context before running, we should be able to dynamically change the available non-scoped variables simply by changing the function's context. That is exactly what I tried to do in the demo below:

  • <!DOCTYPE HTML>
  • <html>
  • <head>
  • <title>Javascript Function() With Context</title>
  • <script type="text/javascript" src="jquery-1.4.2.js"></script>
  • <script type="text/javascript">
  •  
  • // I am a proxy for the Function() constructor that prepends
  • // code to copy the function's context variables into the
  • // function local scope so that they may dynamically changed
  • // at the time of execution.
  • function FunctionContext( sourceCode ){
  •  
  • // Call and return the Function() constructor. Notice
  • // that if the THIS is not Window, we are VAR'ing the
  • // context variables into the function local scope.
  • return(Function(
  • "if (this != window){" +
  • "for (var __i in this ){" +
  • "eval( 'var ' + __i + ' = this[ __i ];' );" +
  • "}" +
  • "}" +
  • sourceCode
  • ));
  • }
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Define a function that uses a variable that is not
  • // defined as part of the function source code.
  • var saySomething = FunctionContext(
  • "console.log( name + ', you\\\'re looking so hot!' );"
  • );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Now, execute the saySomething() method in three different
  • // contexts, each of which changes the name.
  • saySomething.apply({
  • name: "Tricia"
  • });
  •  
  • saySomething.apply({
  • name: "Vicky"
  • });
  •  
  • saySomething.apply({
  • name: "Erika"
  • });
  •  
  • </script>
  • </head>
  • <body>
  • <!--- Intentionally left blank. --->
  • </body>
  • </html>

Here, I have created the function, FunctionContext(), which is essentially a proxy to Javascript's Function() constructor. All it does is prepend a bit of logic to the given source-code before it is passed off to the native Function() constructor. The extra logic checks the "this" context of the function; if the function's context is not the window object, indicating that it has been overridden with a call() or apply() method, all of the this-scoped values are var'd into the compiled function's local scope. In this way, any variable that was in the context at the time of execution is now available as a non-scoped value within the function's logic.

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

Tricia, you're looking so hot!
Vicky, you're looking so hot!
Erika, you're looking so hot!

As you can see, through the use of apply(), I am changing the context of the compiled function for each execution. And, since the first part of that execution copies this-scoped variables into the local scope, the unscoped variable, "name," never causes an error.

I needed a way to dynamically change the variables that were available at the time of a function execution. If I just used the eval() function, I lose the benefit of pre-compiled optimization. If I use just the Function() constructor, I can't really figure out a way to change the available variables without using named arguments (which I may not even know ahead of time). By using both approaches together, however, I think I have found a mostly-elegant way to change the set of available local variables at the time of function execution.




Reader Comments

this is a little over my head but I'm subscribing to the entry to be privy to what the rest of your readers have to add.

Reply to this Comment

Yes, interesting. But at the end it comes to question isn't it too expensive to use eval in such an extent. Probably it is possible to solve this using literal notation or exploating namespaces? I quess.

Reply to this Comment

I thought the article was interesting, but I found the "{{women's name}} you're looking so hot" examples a bit off-putting.

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.