设置变量时,ColdFusion 中的作用域评估顺序是什么?
当使用变量。但是,我在设置变量时找不到有关范围评估顺序的任何信息。
人们会认为它是相同的列表,但似乎有一些警告,如下所示:
<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>
上面的代码试图重用不应该重用的变量名称,但以意想不到的方式失败。
由于 cfset 位于查询循环内,因此范围评估顺序的第 4 项应该用于两者。相反,Next
被评估为 Variables.Next
(第 6 项),然后 Next.id
被评估为 Variables.qryChain .next.id
(第 4 项)并失败。
这有记录在任何地方吗?它只是上面“使用”列表中的第 1-6 项以及一些注意事项吗?这些警告是故意的还是错误?还有哪些其他注意事项?
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 cfset
s 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我想我明白这里发生了什么。您看到的行为来自访问变量以设置它们时的范围搜索。当您设置变量而不对其作用域进行限定时,ColdFusion 将搜索作用域以查看该变量是否首先存在于任何位置,如果存在,则会将其设置在那里。
在第一个示例中:
当您创建“Next”变量时,它实际上将该变量放入 VARIABLES 范围中,如果您在循环过程中随时转储变量范围,则可以证明这一点。您将看到一个带有空结构的“下一个变量”。
问题出在下一行。当您尝试访问 Next 变量以在其中设置新键时,ColdFusion 首先查找查询结果中存在的 Next 变量,因为在循环查询时,查询范围(不是真正的范围,但它的工作方式类似于这种情况)的优先级高于变量范围。该变量不包含结构,因此您将收到有关如何引用它的错误。
范围搜索正在发生,但实际上并不是在设置时发生的,而是在为了设置而访问时发生的。
这是一个演示这一点的工作示例。
在此示例中,我展示了在创建下一个变量后,它确实存在于变量范围中,但是当您立即尝试在其中设置一个键而不限定该访问范围时,您将从查询中的当前记录中获取 NEXT 变量。发生这种情况只是因为查询恰好有一个列名恰好与您尝试使用的变量匹配的记录。
那么为什么 ColdFusion 不尝试将 StructNew() 设置到查询范围(伪范围)中呢?
不能使用点符号来操作查询。再次强调,这并不是一个真正的范围。所以从这个意义上说它是只读的并且被跳过要在 CF 中操作查询结果集,您必须使用查询函数 VARIABLES 作用域。因为未作用域的变量始终放置在 VARIABLES 作用域中。然而,在您尝试设置 id 的行中,后台正在进行另一个阶段,需要首先访问读取容量中的变量,然后尝试执行设置。在这种情况下,它确实会在查询中找到 NEXT 变量,因为首先会进行范围搜索以确定 NEXT 中是否存在要设置该键的值,然后当您尝试为其设置某些内容时,它会失败。至于您的第二组示例,这是预期的行为并且很容易解释。
在第一个示例中,您正在改变变量(将其放置在本地范围内)。然后您将设置该变量的值。当您为变量设置一个值(没有对其作用域进行限定)时,ColdFusion 会检查该变量是否已存在于任何位置(因此它会进行作用域搜索,此时再次进行访问,而不是设置),它将在本地作用域中找到它然后在那里设置值。之后,您再次设置该值,这次正确地确定了它的范围,因此不进行任何搜索。
在第二个示例中,您在最初设置变量时没有对变量进行 varing,它不存在于任何地方,因此它被设置为变量作用域。如果它已经存在于本地作用域中,那么 ColdFusion 会找到它并将其设置在那里(如第一个示例中所示),但由于它不存在,并且没有 var'd,因此它被设置为变量作用域。
最后,在最后一个示例中,您显式定义了变量的范围,因此它将在本地范围中设置。然后,您可以再次设置它,而不对其进行范围界定。 ColdFusion 将在本地范围内找到它并覆盖它。
这个故事的寓意是,调整你的变量范围。获得预期的行为很重要。范围搜索从来都不是一个好主意,但不幸的是,它仍然存在。如果您了解范围搜索的工作方式,我在这里没有看到任何我称之为错误的东西,甚至是不可预测的行为。
I think I understand what is going on here. The behavior you are seeing is from scope searching when accessing variables in order to set them. When you set a variable without scoping it, ColdFusion will search the scopes to see if that variable exists anywhere first, and if it does, it will set it there.
In your first example:
When you are creating your "Next" variable it is actually placing that variable into the VARIABLES scope, you can prove that if you dump the variables scope at any time during the looping process. You will see a "Next variable" with an empty struct.
The problem is in the next line. When you try to ACCESS the Next variable to set a new key into it, ColdFusion is finding the Next variable that exists in the Query result first, because while looping a query the Query scope (not really a scope, but it works like one in this case) has a higher precedence than the Variables scope. That variable does not contain a struct, so you will get an error about how you are referencing it.
Scope searching is happening, but it is not really while setting, it is while accessing in order to set.
Here is a working example to demonstrate this.
in this example, I show that after you create the next variable it does exist in the variables scope, but when you immediately try to set a key into it without scoping that access you will instead get the NEXT variable from the current record in the query. This is only happening because the query happens to have record with a column name that happens to match the variable you are trying to use.
So why doesn't ColdFusion try to set the StructNew() into the Query scope (pseudo-scope)?
The query cannot be manipulated using dot notation. Again, it is not really a scope. So in this sense it is read-only and is being skipped overTo manipulate a query resultset in CF you must use query functionsVARIABLES scope.Because unscoped variables are always placed in the VARIABLES scope. In the line where you try to set the id however, there is another phase going on in the background where it needs to access the variable in a read capacity first, and then try to perform the set. In this case, it DOES find the NEXT variable in the query because scope searching will occur first to determine in NEXT exists to set that key into and then when you try to set something to it, it fails.As for your second set of examples, this is expected behavior and is easily explained.
In the first example, you are varing your variable (which places it in the local scope). You are then setting the value of that variable. When you set a value to a variable (without scoping it) ColdFusion checks to see if that variable already exists anywhere (so it does a scope search, again this is accessing at this point, not setting) it will find it in the local scope and then set the value there. After that you set the value again, this time properly scoping it, so no searching is done.
In the second example, you are not varing the variable when it is initially set, it does not exist anywhere, and so it is set to the variables scope. If it had already existed in the local scope, then ColdFusion would have found it and set it there (as in your first example), but since it did not exist, and it was not var'd it was set to the variables scope.
Finally, in your last example, you explicitly scope your variable and so it will be set in the local scope. You then set it again without scoping it. ColdFusion will find it in the local scope and overwrite it.
The moral of the story is, scope your variables. I tis important to get expected behavior. Scope-searching was never a good idea, but unfortunately, it is here to stay. I don't see anything here that I would call a bug, or even unpredictable behavior if you understand the way scope searching works.
分配期间的范围评估
我知道在 ColdFusion 中创建变量时有两种不同的范围评估方法。我没有测试所有可能的实例,但这就是它应该如何工作。
第一个实例使用评估无作用域变量。创建变量时 cfparam 使用它。如果 ColdFusion 找不到具有给定名称的变量,那么它将在变量范围中创建它。
第二个实例使用评估无作用域变量 然后如果不成功也会在变量范围中创建变量。 cfset 和创建变量的任何其他标记都使用此功能,例如带有
result
属性的cfhttp
和cfsavecontent
的variable< /代码> 属性。
正如您所观察到的,存在奇怪的“有时忽略”查询范围问题。我会将其归类为错误,但有人可能仍然能够提供需要例外的原因。
提升
虽然 ColdFusion 的设计目的是通过多种方式复制 JavaScript(特别是
cfscript
),但存在一个细微的偏差,我还没有看到任何记录。对于函数(脚本和标签),JavaScript 使用提升,而 ColdFusion 则不使用。提升是自动将变量声明移动到函数顶部的过程,同时保持变量赋值的代码放置。这意味着变量的范围在 JavaScript 中不会改变,但在 ColdFusion 中可以。
在 CF9 之前,必须在函数顶部使用 var 关键字,从本质上消除了提升的需要。这与 JavaScript 不同,在 JavaScript 中 var 可以在函数中的任何位置使用并使用提升。 CF9 ColdFusion 采用了随处声明的理念,但忽略了提升的实现。
在下面的两个示例中,JavaScript 仅处理单个作用域,其中
x
是函数本地的。比较:
为了避免由于缺乏提升而造成的潜在陷阱,您可以仅在函数的顶部使用
var
,或者执行 CF9 之前的许多操作并在顶部声明一个 var 结构函数的名称,并用它作为所有变量的前缀(记住不要将其命名为 local)。例如var
与local
在函数中
var
似乎是local
的二等公民代码>范围。如果您尝试使用var
将局部变量设置为与参数相同的名称,您将收到一条错误消息,指出使用 local 定义具有相同名称的局部变量。var
和local
据说是等价的。更多
可能还有其他警告和错误,但我不知道有任何记录在案的案例。
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
cfhttp
with theresult
attribute, andcfsavecontent
svariable
attribute.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
x
being function local.Compared to:
To avoid the potential pitfalls created by the lack of hoisting you can either
var
only 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.var
vslocal
In functions
var
appears to be a second class citizen to thelocal
scope. If you try to set a local variable to the same name as an argument usingvar
you will receive an error message saying Use local to define a local variable with same name. despitevar
andlocal
supposedly being equivalent.More
There may be additional caveats and bugs, however I'm not aware of there being any documented cases.
您可能会收到此错误(因为这是我收到的错误):
这是因为当您处于查询的 cfloop 内时,在评估无范围变量时,查询范围优先于变量范围。但是,当您分配无作用域变量(无论是否在 cfloop 查询上下文内)时,您将使用
variables
作用域。因此,当您分配时,您将“Next”变量设置为
variables.
范围。但是,当您尝试评估 Next (通过引用其键.id
)时,如下所示:现在您将从查询范围中获取
Next
(并且由于 Next 在查询范围不是结构,您会收到错误)。因此,为了尝试回答您的主要问题 - 当您分配未指定范围的变量时,CF 会将变量放入
variables
范围中。当您尝试计算未指定范围的变量时,它将使用 正常范围优先规则来查找匹配项(在本例中,在查询
范围)。You're getting this error, presumably (since it's the error I received):
This is because when you are within a cfloop for a query, the query scope takes precedence over the variables scope when evaluating a scope-less variable. However, when you are assigning scope-less variables (either within a cfloop query context or not) then you use the
variables
scope. So when you assignYou are setting the "Next" variable to the
variables.
scope. However, when you attempt to evaluate Next (by referring to its key,.id
), like so:Now you are getting
Next
from the query scope (and since Next in the query scope is not a structure, you get an error).So, to try to answer your main question - when you are assigning a variable with no scope specified, CF will put the variable in the
variables
scope. When you attempt to evaluate a variable with no scope specified, then it will use the normal scope-precedence rules to find a match (in this case, finding one in thequery
scope).