Skip to main content
This topic has been closed for replies.

2 replies

Participant
October 9, 2012

It is of note that the differentiation between local scopes ('this', 'var' and now 'Local' as built-in local-ish scopes) is not terribly clear here. Here are a few observations about scopes, and their precedence, within a cfc that explicitly explains behaviours, which are hopefully useful observations :

Shared Local/Var scope:

If you declare both a var and a Local such that . A reference to unscoped nCounter will yield 2. If you reverse your declaration order (Local, then var) then you will get 1. So this shared scope appears truly shared. The last declaration of a value via var or Local will win, overriding the preceding value. A cfc throws no issue when you use them interchangeably. You can thus declare a variable via var and then reference it via Local (such that "var nCounter = 0; writeOutput('Local.nCounter: ' & Local.nCounter);" works just fine).

Arguments and Vars:

You cannot, however, use a var'd value that has the same name as an argument that is declared in the function. You will get this error: "[YourVar] is already defined in argument scope."

However, you can use a Local based variable name with the same name as a function declared argument. This seems odd, but that is what you get.

Also, if you declare an Argument after the fact (not as an argument in the function, but inline within the method body such that Argument.NewArgument = 'something'; ), then you are allowed to use this same variable name via either var or Local with no issue and it will be kept separate within the two scopes. You can also access a var'd variable as a structure when using structDelete(). For example, when I clean up a return and want to remove some unnecessary references, I can delete a var'd value by structDelete(Local, 'myUnwantedVar') and then return Local. As far as I can tell, I generally can get a handle on a var via the Local scope.

This:

The this scope is a 'public' scope vs. the Variables which is a 'protected' scope. It is a 'local'-ish scope to the object, but not the method/function in that sense. If you use a constructor to return 'this', the returned object will show the public 'this' based methods but not the protected Variables based values. For example, I generally inject methods upon construction to the Variables method and keep the 'this' scope clean. When you dump your handle to the object, only the public methods are shown and the injected methods keep out of the way but are perfectly accessible to the object and its methods. It is also of great note that it appears that the this space is not searched when you use an unscoped variable reference. Even though I may set a this.myVar, referencing myVar will throw as undefined. Of course, referencing it properly scoped works as expected.

This/Arguments are in Local:

If you dump the Local scope, you will see all your vars in there as well alongside any Local.myVar style variables. You will also notice that Arguments is listed within the Local scope.The 'this' scope will also show up in the Local dump (internally).

Properties:

Properties are a special case and while you can see them in the 'this' scope (if you have some set), you can only read/write them via accessors. However, if you access them via the Variables scope, you can directly read and write to them. The corresponding Property will be updated as well. Creating a Variables element only puts it in the Variables scope, however, as you would expect. They also show up in the Variables scope as regular Variables as well both before and after Variable-scope-based manipulation. Finally, setting a Property via an accessor also updates the Variables version, thus showing they are bound values.

Precedence:

Noting the Arguments before Locals comment above, with which I concur, I would modify the cfc precedence list as such for clarity:

1) Arguments

2) Local & Var (interchangeable)

3)

4)

5) Variables & Property (interchangeable)

6)

7)

9) URL

10) Form

11) Cookie

12) Client

-- The 'this' scope is not searched when an unscoped reference is encountered

-- The 'Request' scope is not searched when an unscoped reference is encountered

-- The 'Application' scope is not searched when an unscoped reference is encountered

-- The 'Session' scope is not searched when an unscoped reference is encountered

Inspiring
October 9, 2012

Arguments and Vars:

You cannot, however, use a var'd value that has the same name as an argument that is declared in the function. You will get this error: "[YourVar] is already defined in argument scope."

However, you can use a Local based variable name with the same name as a function declared argument. This seems odd, but that is what you get.

This represents a bug in my opinion.  The function-local scope should behave identically with or without being qualified with the scope.

-- The 'this' scope is not searched when an unscoped reference is encountered

-- The 'Request' scope is not searched when an unscoped reference is encountered

-- The 'Application' scope is not searched when an unscoped reference is encountered

-- The 'Session' scope is not searched when an unscoped reference is encountered

If you're wanting to be completist about it, you should probably also mention the SERVER scope in that.

And is this doc you seem to be putting together just covering the context of within CFCs?  Because if it's just a "general" one, you also might want to mention these scopes:

  • attributes
  • caller
  • thread

And there's a few other kind-of-almost-scopes-but-not-really like file, thistag, etc too.  I've never really paid attention to how these scopes play out in the scope-look-up though (I always scope all my variables, so it's never important to know).

--

Adam

Participant
October 10, 2012

@Adam,

Yes, originally I was making this for my workgroup as a helpful reference (after finding Local.Something used in a page which really becomes Variables.Local.Something... and trying to explain that it was not quite right and why among some other generally unscoped references ). I thought it might be useful here as for others. And it was more CFC specific. It looks like some of the list got mangled (brackets must be a key entity for links). It did re-iterate the 'kind-of ' scopes per the Adobe list. Let me try again and I will be more inclusive and denote page/cfc/context differences . Maybe someone at Adobe would be kind enough to fix or delete my first attempt.

Anent the Local/argument oddity:

Yes, it does seem like it might be a bug. I suspect it has something to do with the way the 'Local' scope was retro-fitted for 9 . Or it may have been done as a safety to disallow a programmer from creating a same named 'var' variable and then referencing it and actually getting the Argument value of it. Which is what would happen, I think. If you create the same name Local version of an Argument, it is more obvious that an unscoped reference would get the Argument value (as long as you realize the Adobe documentation is wrong and Arguments is searched first). But yes, still a bit odd.

============= EDITED VERSION ============

It is of note that the differentiation between local scopes ('this', 'var' and now 'Local' as built-in local-ish scopes) is not terribly clear here and often there is some confusion for people, especially if they have other language experience (even JavaScript). Here are a few observations about scopes, and their precedence, primarily within a cfc that explicitly explains behaviours, which are hopefully useful observations :

Shared Local/Var scope:

If you declare both a var and a Local such that: var nCounter = 1; Local.nCounter = 2; . A reference to unscoped nCounter will yield 2. If you reverse your declaration order (Local, then var) then you will get 1. So this shared scope appears truly shared. The last declaration of a value via var or Local will win, overriding the preceding value. A cfc throws no issue when you use them interchangeably. You can thus declare a variable via var and then reference it via Local (such that "var nCounter = 0; writeOutput('Local.nCounter: ' & Local.nCounter);" works just fine). Finally, note that 'var' cannot be used outside a function or method. If you do use Local outside a function or method, it is allowed, but you are really created a Variables scoped Local variable such that Local.SomeName would be equivalently referenced by Variables.Local.SomeName within the page. In short, it is pointless and confusing to do so.

Arguments and Vars:

You cannot, however, use a var'd value that has the same name as an argument that is declared in the function. You will get this error: "YourVar is already defined in argument scope."

However, you can use a Local based variable name with the same name as a function declared argument. This seems odd, but that is what you get.

Also, if you declare an Argument after the fact (not as an argument in the function, but inline within the method body such that Argument.NewArgument = 'something'; ), then you are allowed to use this same variable name via either var or Local with no issue and it will be kept separate within the two scopes. You can also access a var'd variable as a structure when using structDelete(). For example, when I clean up a return and want to remove some unnecessary references, I can delete a var'd value by structDelete(Local, 'myUnwantedVar') and then return Local. As far as I can tell, I generally always can get a handle on a 'var' via the Local scope.

This:

The this scope is a 'public' scope vs. the Variables which is a 'protected' scope and Local/var which is roughly the 'private' scope. It is a 'local'-ish scope to the object, but not the method/function in that sense. If you use a constructor to return 'this', the returned object will show the public 'this' based methods but not the protected Variables based values. For example, I generally inject methods upon construction to the Variables method and keep the 'this' scope clean. When you dump your handle to the object, only the public methods are shown and the injected methods keep out of the way but are perfectly accessible to the object and its methods. It is also of great note that it appears that the this space is not searched when you use an unscoped variable reference. Even though I may set a this.myVar, referencing myVar will throw as undefined. Of course, referencing it properly scoped works as expected.

This/Arguments are in Local:

If you dump the Local scope, you will see all your vars in there as well alongside any Local.myVar style variables. You will also notice that Arguments is listed within the Local scope.The 'this' scope will also show up in the Local dump (internally). You can also reference them using Local.this or Local.Arguments.

Properties:

Properties are a cfc special case and while you can see them in the 'this' scope (if you have some set), you can only read/write them via accessors. However, if you access them via the Variables scope, you can directly read and write to them. The corresponding Property will be updated as well. Creating a Variables element only puts it in the Variables scope, however, as you would expect. They also show up in the Variables scope as regular Variables as well both before and after Variable-scope-based manipulation. Finally, setting a Property via an accessor also updates the Variables version, thus showing they are bound values.

Variables:

This is the default scope into which a variable is placed if it is not scoped for both page and CFC contexts. It is the 'protected' scope, meaning it is shared by all the methods within a CFC. In a CFC you should be VERY SURE you want to place a variable into this scope and know why. It is very easy to kill your server if you incorrectly use this scope inside a cfc (especially if you are using singleton or utility classes). In a page, this is the most common place you will be placing your variables.

Precedence:

Noting the Arguments before Locals comment above, with which I concur, I would modify the cfc precedence list as such for clarity with some notes on contextual cases:

1) Arguments scope*

2) Local & Var scopes (interchangeable)*

3) Query Context: Here we mean, typically the tag context. An unscoped reference will look to the query name scope within query output. Note that it appears Arguments and Local scope still trump.

4) Thread Scope & Context**: Threads are a very special case and are very context specific.

5) Variables & Property*** scopes (interchangeable)

6) CGI Scope: The CGI is normally an immutable scope you can only reference but not directly manipulate.

7) File/CFFile Context:

9) URL Scope: ****

10) Form Scope: ****

11) Cookie: ****

12) Client: ****

Unsearched Scopes (Scopes that must always be referenced)

1) this*+

2) Request+

2) Application+

2) Session+

2) Server+

*CFC Only

**Threads are a very special mix of scope and context. I have never tested all the permutations possible (many many many) so I only list the basics of which I know firsthand here: Threads create new Local scope spaces independent from the parent thread (your main request). Your handle to the thread (the name) only lets you get at certain elements that you specify via the thread scope. You can only push references (deep copies) of data to the thread when you create it via its attributes (you can have arbitrary overloaded attributes). The Variables scope inside a thread is shared by all threads spawned by the same parent as well as the parent. Additionally, much of the access threads have to certain scopes is dependent on page request and last thread life. Threads are very powerful and very useful (how else can you kill a hung external web service request?) and I recommend reading up on them.

      • Property is a CFC only scope while Variables is the default scope in both pages and cfc's.

        • These common scopes are available inside and outside of cfc's. They effectively 'break' scope as they can be referenced without scope prefixes inside methods. When I encounter this lack of scoping in a method, I call it 'pulling the values out of the air' to be nice. This is generally considered poor practice to do.

+Available inside and outside a cfc but must be referenced by scope.

May 23, 2012

It seems that either this documentation is inaccurate or that the scoping priority has a bug. "Argument" scope is examined before the "local" scope.

More details here: http://stackoverflow.com/a/10718026/21960