Extending Window To Create A Dynamic Scope Chain For Method Execution In JavaScript

Posted June 1, 2011 at 9:36 AM by Ben Nadel

Tags: Javascript / DHTML

Last week, I talked about using JavaScript's With keyword in order to create a dynamic scope chain that would form the context of a given function's execution. As a follow-up to that post, I wanted to try and accomplish the same kind of thing without having to rely on the With keyword. To do this, I used an approach that I've never thought of before - I extended the global scope (window object); that is, I used "window" as the prototype for my dynamic scope instance.


 
 
 

 
  
 
 
 

One of the fun things about using the with() block in my previous post was that you could use either locally-scoped values or allow the variable references to fall back to the global scope (window). In order to accomplish the same without the with() block, I simply created the "scope" as an extension of the window. In this approach, references can still fall back to the global object; however, this time, the underlying mechanism is the prototype chain, not the scope chain.

To see what I'm talking about, take a look at the following code. Notice that my "scope" variable is being created with Object.create() where "window" is the given prototype.

  • <!DOCTYPE html>
  • <html>
  • <head>
  • <title>Extending Window To Create A Dynamic Scope Chain</title>
  • <script type="text/javascript">
  •  
  •  
  • // I return a function with a "scope" property that can be
  • // used to alter the runtime bindings of the functions.
  • var getFoo = (function(){
  •  
  • // Create the scope for our function. In order to not
  • // use the "with" keyword, we want the scope instance
  • // to extend the global scope. This will allow us to
  • // override scope-based values; or, have them fall
  • // through to the global scope thanks to the prototype
  • // chain.
  • var scope = Object.create( window );
  •  
  • // Define our method. Notice that we are explicilty
  • // scoping our variables to the SCOPE.
  • var getFooMethod = function(){
  •  
  • // Return "foo";
  • return( scope.foo );
  •  
  • };
  •  
  • // Add scope as a property to the method as well so
  • // that it can be access and mutated at runtime.
  • getFooMethod.scope = scope;
  •  
  • // Return the method.
  • return( getFooMethod );
  •  
  • })();
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Store foo in the global name space.
  • this.foo = "Foo In Global.";
  •  
  • // Get closest-scoped foo value.
  • console.log( getFoo() );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Store foo directly in the function scope.
  • getFoo.scope.foo = "Foo In Scope.";
  •  
  • // Get closest-scoped foo value.
  • console.log( getFoo() );
  •  
  •  
  • // -------------------------------------------------- //
  • // -------------------------------------------------- //
  •  
  •  
  • // Update the foo in global scope.
  • window.foo = "UPDATED Foo in Global.";
  •  
  • // Delete the foo from the scope.
  • delete( getFoo.scope.foo );
  •  
  • // Get closest-scoped foo value.
  • console.log( getFoo() );
  •  
  •  
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • Extending Window To Create A Dynamic Scope Chain
  • </h1>
  •  
  • </body>
  • </html>

As you can see, this time, scope is an extension of window. In order to wire this scope into the inner-function, however, we did have to replace unscoped references with explicitly scoped references (ie. "foo" became "scope.foo"). From an external standpoint, however, the logic remained exactly the same.

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

Foo In Global.
Foo In Scope.
UPDATED Foo in Global.

As you can see, since scope extends window, we can selectively add and remove values anywhere we want in scope's prototype chain. This augmentation of the scope object effectively changes the runtime behavior of the getFoo() method.

As I said in my last post, I don't think any of this code has any practical use; I think the only possible value here is in the exploration of how JavaScript works. If nothing else, I think it was kind of fun to extend the actual window object.




Reader Comments

Mar 30, 2012 at 10:41 PM // reply »
1 Comments

This is lovely work, Ben. I'm using a similar thing during an implemention of a stack-based Forthish language in the browser. The justification is this:

> drawGraph

Should use the global stack for binding.

> splitOn standings drawGraph

Not only needs to invoke drawGraph as many times as standings splits down to, but needs to provide a different, filtered, stack to it each time.

Just wanted to let you know that somebody else was crazy enough to find a practical reason to use dynamic scoping in JS :D

c


Mar 31, 2012 at 9:26 AM // reply »
11,241 Comments

@Chris,

Ha ha, glad to see that someone out there appreciates my strange experimentation with getting JavaScript to do weird stuff :)


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 22, 2013 at 12:44 PM
Ask Ben: Query Loop Inside CFScript Tags
In cf10, if you call a function that has: local.result = {}; local.result.msg = ""; local.svc = new query(); local.svc.setSQL("SELECT * FROM..."); local.obj = local.svc.exe ... read »
May 22, 2013 at 12:29 PM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Ben: What version of Java are you using? Also, did you test users.id to see what Java reports as the data type? I wonder if it's not a Java primitive data type, but getting returned as something ... read »
May 22, 2013 at 11:47 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Dana, Awesome - so it looks like this bug was fixed in ColdFusion 10. Thanks so much for double-checking that. ... read »
May 22, 2013 at 11:37 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
When I c&p and run on cf10, I get: Selected User IDs: 1,4 User 1 selected: YES - YES User 2 selected: NO - NO User 3 selected: NO - NO User 4 selected: YES - YES User 5 selected: NO - ... read »
May 22, 2013 at 11:27 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
@Tom, Good thought, but no dice. Both of these still exhibit the same behavior: users.id[ users.currentRow ] users[ "id" ][ users.currentRow ] It's just something whacky happening with ... read »
May 22, 2013 at 11:07 AM
Strange Interaction Between DeserializeJson(), ArrayContains(), And Database Values In ColdFusion
Could your problem be that "users.id" is actually an ARRAY, not a single value? Perhaps try it again with "users.id[1]" (I only have CF8 here at work). ... read »
May 22, 2013 at 7:52 AM
Nested Views, Routing, And Deep Linking With AngularJS
Hi, Just a quick thank you. As it happens, for my own purposes, the pending ui-router work being done in native angular is likely the one I'll adopt, but your exploration, code and documentation of ... read »
May 22, 2013 at 4:43 AM
How Do You Use The ColdFusion CFParam Tag?
'<cfparam>' or 'isDefined()and <cfset>' performs the same task.Is there any difference? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools