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 CFUNITED 2010 (Landsdown, VA) with:

THIS Is Just A Locally-Scoped Variable Inside Of ColdFusion User Defined Functions

Posted by Ben Nadel
Tags: ColdFusion

Yesterday, when I was coding my this.jarPaths application-specific URLClassLoader proof-of-concept, I stumbled upon a very interesting feature of ColdFusion. To make the proof-of-concept work, I had to take a user defined function (UDF) located within the Application.cfc component and store a reference to it in the global URL scope. However, even when this UDF was invoked off of the URL scope, I wanted it to execute as if it were still in the context of the Application.cfc. Since closures don't really exist in ColdFusion, I attempted to fake this lexical binding by overriding the THIS variable at the beginning of the UDF:

  • <!--- Create a locally-scoped, fake THIS reference. --->
  • <cfset var this = url.createJavaContext />

I didn't know if this would work since "this" is technically a keyword in ColdFusion, but I figured it was worth a shot. When I executed this code, I got the following ColdFusion error:

Cannot declare local variable this twice. Local variables cannot have the same names as parameters or other local variables.

Now, this is very interesting! ColdFusion is not throwing an error because I'm trying to overwrite the THIS variable - ColdFusion is only throwing an error because there is already a locally-scoped THIS variable. The key take-away here is that inside of a user defined function (UDF), the THIS scope is not really a scope - it's a variable. And, if it's a variable, we can overwrite it!

Being able to overwrite the THIS variable on a per-UDF basis opens up some very interesting opportunities. In my this.jarPaths proof-of-concept, I overwrote the THIS variable in my stand-alone createJava() method in order to help it execute as if it were still in the context of the Application.cfc ColdFusion component. When dealing with bound-methods, however, I believe that overwriting the THIS variable can fix one of the most fundamental flaws in the ColdFusion component's public-private scoping.

In ColdFusion components, the public scope is referred to as THIS while the private scope is referred to as VARIABLES. If you're someone like me, who's obsessive-compulsive about scoping all values in ColdFusion, this divergence between THIS and VARIABLES forces one to tightly couple a variable's access permissions to the code that references it. In other words, when I reference a component method, my referencing code must explicitly invoke either the public or private namespace. The problem with this is that if I ever change the access to one of my methods (ie. going from Public to Private), I will also have to change all of the internal, encapsulated code that references it.

In ColdFusion, there's another very interesting feature that comes into play here: all of a component's methods are available in the private scope (Variables); this includes both publicly and privately declared methods (does anyone ever use Package?). We can take this feature and the fact that THIS is a locally-declared variable, and really start to have some fun with it.

To demonstrate, I have created a Girl.cfc ColdFusion component with one public method - sayHello() - and one private method - getMood():

Girl.cfc

  • <cfcomponent
  • output="false"
  • hint="I am a girl object.">
  •  
  •  
  • <cffunction
  • name="sayHello"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="I say hello to the given person.">
  •  
  • <!---
  • How we say this depends on our mood. Check to see what
  • our current mood is and behave appropriately.
  • --->
  • <cfif (this.getMood() gt 5)>
  •  
  • <!--- Good mood! Be nice. --->
  • <cfreturn "Hey there, great to meet you!" />
  •  
  • <cfelse>
  •  
  • <!--- Bad mood, rip his head off. --->
  • <cfreturn "For real? What makes you think that a girl like Me would want to talk to a guy like You? I mean, really? Did you honestly think this would work out? No no, I'm serious. Do you see my body? Do you see my perfectly manicured nails and my perfectly highlighted hair? I don't spend hours making all of 'this' (gestures to face) happen so that guys like you will take notice. What were you even thinking?" />
  •  
  • </cfif>
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="getMood"
  • access="private"
  • returntype="numeric"
  • output="false"
  • hint="I return the mood of the girl.">
  •  
  • <!---
  • For mood, let's just return a random value between 1 and
  • 10 (1 being not happy, 10 being ecstatic).
  • --->
  • <cfreturn randRange( 1, 10 ) />
  • </cffunction>
  •  
  • </cfcomponent>

As you can see, the public method, sayHello(), makes use of the private method, getMood(), when determining what value to return to the calling context. Then, I created a simple test script to make sure this was all working:

  • <!--- Create an instance of our girl. --->
  • <cfset girl = createObject( "component", "Girl" ) />
  •  
  • <!--- Say hello to the girl. --->
  • <cfoutput>
  •  
  • Ben: Good morning, how's it going?<br />
  •  
  • Girl: #girl.sayHello()#
  •  
  • </cfoutput>

When I run this code, I get the following ColdFusion error:

The method getMood was not found in component Girl. Ensure that the method is defined, and that it is spelled correctly.

The problem here, as I alluded to before, is that the getMood() method is private but I have attempted to reference it via the public scope (THIS):

  • <cfif (this.getMood() gt 5)> ... </cfif>

Like I said, if you are compelled to scope your values in ColdFusion, your method references become tightly coupled to their access permissions.

In a more traditional programming language, there is no syntactic difference between public and private values; "public" and "private" keywords are used to denote the permissions while all values can still be referenced internally via the "this" pointer. In ColdFusion, we can start to mimic that behavior somewhat within UDFs by actually pointing the locally-scoped THIS variable to the component's private VARIABLES scope:

Girl.cfc (With THIS Variable Being Overwritten)

  • <cfcomponent
  • output="false"
  • hint="I am a girl object.">
  •  
  •  
  • <cffunction
  • name="sayHello"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="I say hello to the given person.">
  •  
  • <!---
  • Here, we are overwriting the local THIS "variable" to
  • point to the component's private Variables scope. In
  • doing this, all references to THIS within this method
  • will actually be referencing the component's private
  • scope. Since all method are avialable in the private
  • scope, this works wonderfully.
  • --->
  • <cfset this = variables />
  •  
  • <!---
  • How we say this depends on our mood. Check to see what
  • our current mood is and behave appropriately.
  • --->
  • <cfif (this.getMood() gt 5)>
  •  
  • <!--- Good mood! Be nice. --->
  • <cfreturn "Hey there, great to meet you!" />
  •  
  • <cfelse>
  •  
  • <!--- Bad mood, rip his head off. --->
  • <cfreturn "For real? What makes you think that a girl like Me would want to talk to a guy like You? I mean, really? Did you honestly think this would work out? No no, I'm serious. Do you see my body? Do you see my perfectly manicured nails and my perfectly highlighted hair? I don't spend hours making all of 'this' (gestures to face) happen so that guys like you will take notice. What were you even thinking?" />
  •  
  • </cfif>
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="getMood"
  • access="private"
  • returntype="numeric"
  • output="false"
  • hint="I return the mood of the girl.">
  •  
  • <!---
  • For mood, let's just return a random value between 1 and
  • 10 (1 being not happy, 10 being ecstatic).
  • --->
  • <cfreturn randRange( 1, 10 ) />
  • </cffunction>
  •  
  • </cfcomponent>

Since a component's Variables scope contains both the public and private methods, pointing the method's THIS variable at the Variables scope provides THIS-based access to all component methods. This time, when we run the test code, we get the following page output:

Ben: Good morning, how's it going?

Girl: For real? What makes you think that a girl like Me would want to talk to a guy like You? I mean, really? Did you honestly think this would work out? No no, I'm serious. Do you see my body? Do you see my perfectly manicured nails and my perfectly highlighted hair? I don't spend hours making all of 'this' (gestures to face) happen so that guys like you will take notice. What were you even thinking?

As you can see, by overwriting the local THIS variable, we can now refer to both our public and private methods using the "this" namespace.

Now, I happen to love this concept, but it does come with some serious caveats. For starters, prior to ColdFusion 9, private methods cannot be invoke with named arguments. As such, once you overwrite the THIS variable, trying to do something like this:

  • <cfset this.doSomething( foo = "bar" ) />

... will result in the following ColdFusion error:

Cannot invoke method doSomething on an object of type coldfusion.runtime.VariableScope with named arguments. Use ordered arguments instead.

In ColdFusion 9, however, this behavior has been fixed; both public and private methods can be invoke using either named or ordered arguments.

Also, you will lose direct access to non-UDF, this-scope values. While it is typically recommended that all values in a component be made private (with UDFs being made public only as needed), there are occasions when you might want a public property (such as with a class "Constant"). In these cases, if you overwrite the THIS variable, you'll have to access the original THIS scope by way of the private scope:

"this.this.YOUR_CONSTANT"

A component's private scope also contains a reference to the public scope. But, since we are overwriting our local THIS variable within a UDF, getting to the original THIS scope requires digging a little deeper.

NOTE: This is only relevant to the local context of the UDF. To the external world, there is absolutely no change in the behavior of the public and private scopes.

You might look at this and think - Dude, that's downright crazy, unmaintainable, and hella confusing. And you might have a point. But, one of the primary reasons that I don't make any of methods private is the fact that they are no longer accessible via the THIS "scope." By re-routing the local THIS variable to point to the private scope, we can finally create a behavior that is more inline with traditional programming languages.




Reader Comments

Interesting stuff, although I would probably recommend getting over your OCD and not scoping your call to getMood() like most sane people would do. :P

Once again a thought-provoking post. Nice work.

Reply to this Comment

I prefer the way Railo handles this.

Railo correctly evaluates the context of the function calls allowing you to reference private and package methods through this.

Reply to this Comment

@Brett,

Agreed - that is exactly what I am *trying* to do here. It feels like am much more natural use of "this".

Reply to this Comment

You're currently viewing This and Variables as Public vs Private. For ColdFusion I think the more accurate way to view them is as External vs Internal.

The solution to your OCD is to always prefix methods of the same class with Variables; not to "fix" This.

A slight error on private methods cannot be invoke with named arguments prior to CF9, private methods can be invoked with named arguments as long as the Variables scope isn't used. Drop the scope all all is good.

But, one of the primary reasons that I don't make any of methods private is the fact that they are no longer accessible via the THIS "scope."

This made me cry. That's such a bad excuse for not over exposing your methods.

Reply to this Comment

i share your pain when it comes to scoping.
coming from java, first thing i tried when writing a cfc was to reference a cfc method with this.

but what do you think about appending the methods instead of replacing the variable ?
<cfset structAppend( this, variables, false ) />

Reply to this Comment

@Fan,

My reasons are not always great; but, I simply don't like typing "variables". Something about it just feels wrong to me. In any case, always using Variables or hacking the Variables scope still causes the problem that this-scoped non-UDF values are not in the variables scope. Only UDFs get placed in both internal/external scopes.

As far as making things overly public, I understand this from a philosophical point of view; but, it has never caused problems for me. But then again, I am not an OOP master by annnnnny means :)

Like I said, if I could use "this" everywhere, I'd definitely make things more private.

@Nelle,

If you're gonna use structAppend() to attach the variables scope to the public scope, you might as well just make everything public (seeing as this will affect the external view as well as the internal view).

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.