设置变量时,ColdFusion中的范围评估顺序是什么?

时间:2012-02-10 16:10:56

标签: coldfusion coldfusion-9

using variables时,范围评估顺序是众所周知/记录的。但是,在设置变量时,我找不到有关范围评估顺序的任何信息。

人们会认为它是相同的列表,但似乎有一些警告如下所示:

<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项,但有一些注意事项?这些警告是故意还是错误?还有什么其他警告?

3 个答案:

答案 0 :(得分:5)

我想我明白这里发生了什么。您正在查看的行为是在访问变量时进行范围搜索以设置它们。当您设置一个变量而不进行范围设定时,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>

当您创建“Next”变量时,它实际上是将该变量放入VARIABLES范围,您可以证明如果在循环过程中随时转储变量范围。您将看到一个带有空结构的“Next变量”。

问题出在下一行。当您尝试访问Next变量以将新键设置到其中时,ColdFusion首先查找查询结果中存在的Next变量,因为在循环查询时查询范围(不是真正的范围,但它的作用类似于这种情况)具有比变量范围更高的优先级。该变量不包含结构,因此您将收到有关如何引用它的错误。

范围搜索正在进行中,但设置时并非如此,它是在访问时设置的。

这是一个展示这一点的工作示例。

<cfset qryChain = queryNew("id,next")>
<cfset queryAddRow(qryChain, 3) />
<cfdump var="#qryChain#">
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
    <cfset Next = StructNew()>
    <cfdump var="#variables#">
    <cfset Next.id = qryChain.next>
    <cfdump var="#qryChain#">

</cfloop>

在这个例子中,我展示了在创建下一个变量之后它确实存在于变量范围中,但是当你立即尝试设置一个键而没有确定访问范围时,你将从当前记录中获取NEXT变量在查询中。这只是发生,因为查询碰巧有一个列名称的记录恰好与您尝试使用的变量匹配。

那么为什么ColdFusion不尝试将StructNew()设置为Query范围(伪范围)? 无法使用点表示法操作查询。同样,它不是一个范围。所以在这个意义上它是只读的并且被跳过 要在CF中操作查询结果集,必须使用查询函数VARARBLES范围。因为未编组的变量总是放在VARIABLES范围内。但是,在您尝试设置id的行中,在后台还有另一个阶段,它需要首先以读取容量访问变量,然后尝试执行该设置。在这种情况下,它会在查询中找到NEXT变量,因为范围搜索将首先在NEXT中确定是否存在以将该键设置为,然后当您尝试设置该键时,它会失败。

至于你的第二组例子,这是预期的行为并且很容易解释。

在第一个示例中,您将变量变量(将其置于本地范围内)。然后,您将设置该变量的值。当你为变量设置一个值(没有确定范围)时,ColdFusion会检查该变量是否已经存在于任何地方(因此它会进行范围搜索,此时此时正在访问,而不是设置)它会在本地范围内找到它然后在那里设置值。之后再次设置该值,这次正确确定范围,因此不进行搜索。

在第二个示例中,您在初始设置时不变量变量,它在任何地方都不存在,因此它被设置为变量范围。如果它已经存在于本地范围内,那么ColdFusion会找到它并将其设置在那里(如第一个例子中所示),但由于它不存在,并且它不是var,因此它被设置为变量范围。

最后,在上一个示例中,您明确地对变量进行了范围调整,因此它将在本地范围内设置。然后再次设置它而不确定范围。 ColdFusion会在本地范围内找到它并覆盖它。

故事的寓意是,范围你的变量。得到预期的行为我很重要。范围搜索从来都不是一个好主意,但不幸的是,它仍然存在。如果您了解范围搜索的工作方式,我在这里看不到任何我会称之为错误的内容,甚至是不可预测的行为。

答案 1 :(得分:3)

分配期间的范围评估

在ColdFusion中创建变量时,我知道两种不同的范围评估方法。我没有测试过每个可能的实例,但这是它应该如何工作的。

第一个实例使用evaluating unscoped variables中的完整范围列表。 cfparam在创建变量时使用它。如果ColdFusion没有找到具有给定名称的变量,那么它将在Variables范围中创建它。

第二个实例使用evaluating unscoped variables中的前6个范围,如果不成功,还将在变量范围中创建变量。 cfset和任何其他创建变量的标记都会使用此标记,例如cfhttp具有result属性和cfsavecontent s variable属性。

正如您所观察到的,奇怪的是“有时会忽略”查询范围问题。我会将其归类为一个错误,但有人可能仍然能够提供为什么需要进行异常的原因。

<强>起重

虽然ColdFusion旨在以多种方式复制JavaScript(特别是cfscript),但是有一个微妙的偏差,我没有看到记录。关于函数(脚本和标记),JavaScript使用hoisting,而ColdFusion不使用。

提升是将变量声明自动移动到函数顶部的过程,同时保持变量赋值的代码放置。这意味着变量的范围在JavaScript中不会改变,但它可以在ColdFusion中。

在CF9之前,必须在函数的顶部使用var关键字,基本上不需要提升。这与JavaScript不同,其中var可以在函数中的任何位置使用并使用提升。随着CF9 ColdFusion采用声明任何地方的哲学,但忽略了实施吊装。

在以下两个示例中,JavaScript只处理单个范围,x是本地函数。

<!--- sets variables in 1 scope --->
<cfscript>
    var x = 0;
    x = 1;
    local.x = 7;
</cfscript>

与:相比:

<!--- sets variables in 2 scopes --->
<cfscript>
    x = 1;
    var x = 0;
    local.x = 7;
</cfscript>

为了避免由于缺少提升而产生的潜在缺陷,您可以var仅在函数的顶部,或者执行CF9之前的许多事情并在函数顶部声明var结构并为所有变量添加前缀(记住不要将其命名为local)。 e.g。

<cfset var localVars = StructNew()>
<cfset localVars.x = 7>
<cfset localVars.y = 1>

var vs local

在函数中var似乎是local范围的第二类公民。如果您尝试使用var将局部变量设置为与参数相同的名称,您将收到一条错误消息,指出使用local来定义具有相同名称的本地变量。尽管{{1 }}和var据说是等价的。

更多

可能还有其他警告和错误,但我不知道有任何记录在案的情况。

答案 2 :(得分:0)

您可能会收到此错误(因为它是我收到的错误):

You have attempted to dereference a scalar variable of type class coldfusion.sql.QueryColumn as a structure with members.

这是因为当您在查询的cfloop中时,查询范围在评估无范围变量时优先于变量范围。但是,当您分配无范围变量时(或者在cfloop查询上下文中),您可以使用variables范围。所以当你指定

<cfset Next = StructNew()>

您正在设置&#34;下一步&#34;变量到variables.范围。但是,当您尝试评估Next(通过引用其键.id)时,如下所示:

<cfset Next.id = qryChain.next>

现在您从查询范围获取Next(并且由于查询范围中的Next不是结构,因此会出错)。

因此,要尝试回答您的主要问题 - 当您分配未指定范围的变量时,CF会将变量放在variables范围内。当您尝试评估未指定范围的变量时,它将使用normal scope-precedence rules查找匹配项(在这种情况下,在query范围内查找一个匹配项。)