ColdFusion 10 Beta - Closures And Components And The THIS Scope

Posted February 22, 2012 at 9:29 AM by Ben Nadel

Tags: ColdFusion

The last couple of days have been really exciting as we've been exploring the new Closures and Function Expressions that have been added to ColdFusion 10. First, we looked at the general structure and behavior of closures and function expressions; then, we looked at how closures interact with threads; now, I'd like to take a quick look at how closures interact with ColdFusion components (CFC).

THIS Is Never That Simple - Runtime Execution Context Bindings

When you invoke a function, it gets invoked in an "execution context." This context is defined by three different characteristics:

  • Variables environment
  • Lexical environment
  • This-scope binding

As we've seen in the last few blog posts, the lexical environment is quite powerful when it comes to closures. It allows us to retain references to define-time values, even after our closures have been passed out of scope. Up until now, we've not had to worry about any THIS-scope binding since all of our code has been written without ColdFusion components. If I've learned anything from JavaScript, however, it's that the THIS scope is never as simple as you'd like it to be. For this post, I want to explore the binding of the THIS scope when closures are used in conjunction with ColdFusion components.


 
 
 

 
  
 
 
 

For the first test, all I want to do is see how the THIS and VARIABLES scope are bound to a closure that is defined inside a ColdFusion component. When that closure is passed out of the component, what can it see? To investigate, I've created a simple ColdFusion component that defines an accessor and returns it to the calling context:

Simple.cfc - Our Simple ColdFusion Component

  • component
  • output = "false"
  • {
  •  
  •  
  • // Before we return our closures, let's set some values in the
  • // public and private scopes.
  • this.value = "I am in the THIS scope!!";
  • variables.value = "I am in the VARIABLES scope!!";
  •  
  •  
  • // I return a closure that attempts to access the THIS and
  • // VARIABLES of the current component. We can test to see how
  • // these are bound at runtime.
  • function getClosure(){
  •  
  • // Define a local version of the "THIS" scope. This is a
  • // common practice in languages like JavaScript where the
  • // THIS scope is dynamically bound at method invocation time.
  • // THIS is treated differently than other variables... is the
  • // same true in ColdFusion??
  • var self = this;
  •  
  • // Create our closure accessor.
  • var getter = function( scope, key ){
  •  
  • // Check the target scope for access.
  • if (scope == "this"){
  •  
  • return( this[ key ] );
  •  
  • } else if (scope == "variables"){
  •  
  • return( variables[ key ] );
  •  
  • } else {
  •  
  • return( self[ key ] );
  •  
  • }
  •  
  • };
  •  
  • // Return the new closure accessor function.
  • return( getter );
  •  
  • }
  •  
  •  
  • }

As you can see, this ColdFusion component defines the closure, getter(). This getter then provides tunneling access to the This scope, the Variables scope, and the "Self" scope. Self is just a local variable that points to the This scope. If you've done any coding in JavaScript, you've probably come across this pattern at some point. Since the This scope is dynamically bound in JavaScript, people often create a "self" variable to retain a reference to a specific version of This.

Now, let's create some external code that makes use of this getter():

  • <!---
  • Start a CFScript block. Closures can only be used inside
  • of CFScript due to the syntax required to define them.
  • --->
  • <cfscript>
  •  
  •  
  • // Create an instance of our new component and access the
  • // closure "Tunnel" that we've tried to create.
  • getter = new Simple().getClosure();
  •  
  • // Access the public and private values.
  • writeOutput( "Public: " & getter( "this", "value" ) );
  • writeOutput( "<br />" );
  • writeOutput( "Private: " & getter( "variables", "value" ) );
  • writeOutput( "<br />" );
  • writeOutput( "Self: " & getter( "self", "value" ) );
  •  
  •  
  • </cfscript>

Here, we are requesting the closure from our Simple.cfc instance. We are then using that closure to access the "value" property from each of the three scopes. When we run this code, we get the following page output:

Public: I am in the THIS scope!!
Private: I am in the VARIABLES scope!!
Self: I am in the THIS scope!!

As you can see, the closure, defined within the ColdFusion component, retained a reference to the appropriate This and Variables scope even when that closure was passed out of context.

But, the closure was defined in a context in which "This" is a meaningful value; and it was then passed into a context in which "This" is not a meaningful value. As such, we can't be quite convinced that the This scope binding was truly put to the test. In order to put this binding through the wringer, we have to pass the closure into a context in which the This scope binding is meaningful. In other words, we have to pass the closure into another ColdFusion component.

For this experiment, I've created another simple ColdFusion component that does nothing but provide potential override values for the This-scope and Variables-scope:

Simple2.cfc - Our Secondary ColdFusion Component

  • component
  • output = "false"
  • {
  •  
  •  
  • // For this test, we'll SET the methods from the outside.
  • // But, we want to see if our local values will override the
  • // closure value bindings.
  • this.value = "I am in the OVERRIDE THIS scope.";
  • variables.value = "I am in the OVERRIDE VARIABLES scope!!";
  •  
  • // GETTER will be injected {here}.
  •  
  •  
  • }

As you can see, this component does almost nothing. That's because our calling code is going to be injecting the above-mentioned getter() method into this new component context. Once the getter() method becomes a property of this secondary component, we can really put the This-binding to the test:

  • <!---
  • Start a CFScript block. Closures can only be used inside
  • of CFScript due to the syntax required to define them.
  • --->
  • <cfscript>
  •  
  •  
  • // Create an instance of our new component and access the
  • // closure "Tunnel" that we've tried to create.
  • getter = new Simple().getClosure();
  •  
  • // Now, let's create out second component, that has not methods.
  • override = new Simple2();
  •  
  • // Store the getter closure IN the override component. We just
  • // want to see if the transfer does anything with the runtime
  • // bindings of scopes.
  • override.getter = getter;
  •  
  • // Now, let's try to access the public and private scopes from
  • // the FIRST component when calling the accessor ON the SECOND
  • // component.
  • writeOutput(
  • "Public: " & override.getter( "this", "value" )
  • );
  • writeOutput( "<br />" );
  • writeOutput(
  • "Private: " & override.getter( "variables", "value" )
  • );
  • writeOutput( "<br />" );
  • writeOutput(
  • "Self: " & override.getter( "self", "value" )
  • );
  •  
  •  
  • </cfscript>

Here, we are repeating the same scope-access test as before; only this time, we are performing it after the closure was transfered from one This-relevant context into another This-relevant context. When we run the above code, we get the following output:

Public: I am in the OVERRIDE THIS scope.
Private: I am in the VARIABLES scope!!
Self: I am in the THIS scope!!

Here's where things start to get a bit more interesting. If you look at the first line of output, you'll notice that the public value - the This-scoped value - came out of the second ColdFusion component, not the first. This means that when the closure was executed in the context of the second component, the runtime binding of THIS was bound to the second component, not to the lexical environment of the closure.

"This" is a magical scope. Its binding is performed just-in-time as functions are executed. We can't depend on the lexical scoping of our closures to retain the appropriate reference. If we need to reference the This scope, we can always use the "Self" pattern. As you can see from the above output, our locally-defined Self reference was able to retain the original This scope reference, even when the This scope was being dynamically bound.

Closures are pretty awesome. They have the ability to retain references to their lexical environment even when they are passed out of scope. This is true even for the define-time page context and the Variables scope. The only thing that seems to break that pattern is the runtime binding of the This scope. This aspect of the execution context is always dynamic, just as it is in JavaScript.


You Might Also Be Interested In:



Reader Comments

Feb 22, 2012 at 2:14 PM // reply »
5 Comments

Hey Ben, it is funny you post this as I just ran into an issue here.

http://tylerclendenin.com/2012/02/coldfusion-zeus-10-beta-function-expressions-and-closures-not-quite-there/

I posted two bug reports based on those things, originally I thought that the this scope was broken when calling function expressions in a CFC's context, but your post lead me to reevaluate that and it looks to me more that the invoke function does not properly apply the context.


Feb 22, 2012 at 2:36 PM // reply »
11,238 Comments

@Tyler,

What we really need is our friendly call() and apply() methods from JavaScript ! That would be sweet. I haven't played with the invoke() method, but I assume it's just the CFScript-based equivalent of the CFInvoke tag and uses a "String" for method name. Would be interesting to see something like:

  • call( instance, closure, arg, arg, arg )

.. kind of a thing.

And to be fair, even in JavaScript, many people argue that the this-behavior *is* a bug. I think the next version of JavaScript may actually change the way it works (or so I've heard in passing).


Feb 23, 2012 at 11:01 AM // reply »
8 Comments

I'm probably behind the curve on this, but could this feature be used to call methods on a CFC dynamically?

Instead of having a syntax like

<cfset results = arrayNew(1) />
<cfset myObj = createObject("component", "cfcs.foo" />

<cfloop list="#methodNames#" index="currentMethod">
<cfset arrayAppend(results, myObj[currentMethod]() />
</cfloop>

Instead we could define a getMethod() on the cfcs.foo CFC that would use this closure syntax?

I dunno, thinking out loud here. But if we could pull a reference to a CFC method dynamically, that would definitely make some things more interesting and potentially more elegant...


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 17, 2013 at 7:42 PM
HashKeyCopier - An AngularJS Utility Class For Merging Cached And Live Data
Ben - thanks so much for posting these Angular articles and findings, they've been a huge help towards learning one of the more 'complex' JavaScript frameworks out there (IMO). I have been using Angu ... read »
May 16, 2013 at 5:01 PM
UPDATE: Parsing CSV Data Files In ColdFusion With csvToArray()
Your code was the closest thing I've found to obtaining some direction for converting ISO fields to values that CF can translate properly. Thank you for posting! ... read »
May 15, 2013 at 10:37 PM
Very Simple Pusher And ColdFusion Powered Chat
hi id making plz easy ... read »
May 15, 2013 at 6:07 PM
Making SOAP Web Service Requests With ColdFusion And CFHTTP
Ben, you once again saved my bacon at work. Thank you, thank you, thank you! ... read »
May 15, 2013 at 4:15 PM
What If All User Interface (UI) Data Came In Reports?
@Josh, Thanks! @Ben, I definitely recommend the David West book "Object Thinking" I've been quoting from. It goes deeply into the philosophy and history of OO programming. His breadth ... read »
May 15, 2013 at 11:36 AM
Ask Ben: Print Part Of A Web Page With jQuery
I found this helpfull when you need to keep (refresh) the original parent page after closing the iframe child print dialog (Hoping you're not using a form at this time so it won't submit again): On ... read »
May 14, 2013 at 7:13 PM
What If All User Interface (UI) Data Came In Reports?
@Jonah, If there's any books you'd recommend on the subject of domain modelling, I'd love to hear it. I just downloaded the free PDF of "Domain Driven Design Quickly". Figured I'd give it ... read »
May 14, 2013 at 6:57 PM
The UX Of Prototyping: Low-Fidelity Is The New High-Fidelity
@Phillip, I'm not sure I follow what you mean? Are you saying that you looked at the list of widgets provided by the jQuery UI and let that be your style guide? ... read »
InVision App - Prototyping Made Beautiful With Prototyping Tools