The scope evaluation order is well known/documented when using variables. However I can’t find any information about the scope evaluation order when setting a variable.
One would presume it’s the same list, but there appears to be a few caveats as demonstrated here:
<cfset qryChain = queryNew("id,next")>
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
<cfset Next = StructNew()>
<cfset Next.id = qryChain.next>
</cfloop>
The above code is trying to reuse a variable name that it shouldn’t, but fails in an unexpected way.
Since the cfsets are inside a query loop, item 4 of the scope evaluation order should be used for both. Instead Next is being evaluated as Variables.Next (item 6), and then Next.id is being evaluated as Variables.qryChain.next.id (item 4) and fails.
Is this documented anywhere? Is it simply items 1-6 of the “using” list above with a few caveats? Are these caveats intentional, or bugs? What other caveats are there?
Scope evaluation during assigment
I am aware of two different methods of scope evaluation when creating variables in ColdFusion. I haven’t tested every possible instance, but this is how it should work.
The first instance uses the full list of scopes in evaluating unscoped variables. This is used by cfparam when creating variables. If ColdFusion does not find a variable with the given name then it will create it in the Variables scope.
The second instance uses the first 6 scopes in evaluating unscoped variables and then if unsuccessful will also create the variable in the Variables scope. This is employed by cfset and any other tags that creates variables, such as
cfhttpwith theresultattribute, andcfsavecontentsvariableattribute.As you observed there is the odd “sometimes ignore” the query scope issue. I would classify that as a bug, but someone may yet be able to provide a reason why the exception needs to be made.
Hoisting
Although ColdFusion was designed to copy JavaScript in a lot of ways (specifically
cfscript) there is a subtle deviation which I haven’t seen documented. With regard to functions (both script and tag), JavaScript employs hoisting, whereas ColdFusion does not.Hoisting is the process of automatically moving the declaration of a variable to the top of the function, while maintaining the code placement of the assignment of the variable. The means that the scope of a variable will not change in JavaScript, but it can in ColdFusion.
Prior to CF9 the var keyword had to be used at the top of the function, essentially negating the need for hoisting. This is different from JavaScript, where var can be used anywhere in a function and employs hoisting. With CF9 ColdFusion adopted the declare anywhere philosophy, but neglected to implement hoisting.
In both the following examples JavaScript would only be dealing with a single scope, with
xbeing function local.Compared to:
To avoid the potential pitfalls created by the lack of hoisting you can either
varonly at the top of the function, or do things as many have prior to CF9 and declare a var structure at the top of the function and prefix all variables with that (remembering to not name it local). e.g.varvslocalIn functions
varappears to be a second class citizen to thelocalscope. If you try to set a local variable to the same name as an argument usingvaryou will receive an error message saying Use local to define a local variable with same name. despitevarandlocalsupposedly being equivalent.More
There may be additional caveats and bugs, however I’m not aware of there being any documented cases.