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

nos*_*leg 8 coldfusion coldfusion-9

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

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

<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>
Run Code Online (Sandbox Code Playgroud)

上面的代码试图重用它不应该的变量名,但是以一种意想不到的方式失败.

由于cfsets在查询循环中,因此范围评估顺序的第4项应该用于两者.而是Next被评估为Variables.Next(项目6),然后Next.id被评估为Variables.qryChain.next.id(项目4)并且失败.

这记录在哪里?它只是上面"使用"列表的第1-6项,但有一些注意事项?这些警告是故意还是错误?还有什么其他警告?

Jas*_*ean 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>
Run Code Online (Sandbox Code Playgroud)

当您创建"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>
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

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


nos*_*leg 3

分配期间的范围评估

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

第一个实例在计算无作用域变量时使用完整的作用域列表。创建变量时 cfparam 使用它。如果 ColdFusion 找不到具有给定名称的变量,那么它将在变量范围中创建它。

第二个实例使用前 6 个作用域来评估无作用域变量,如果不成功,也会在变量作用域中创建变量。这是由 cfset 和创建变量的任何其他标签(例如cfhttp使用result属性和cfsavecontentsvariable属性)使用的。

正如您所观察到的,存在奇怪的“有时忽略”查询范围问题。我会将其归类为错误,但有人可能仍然能够提供需要例外的原因。

吊装

尽管 ColdFusion 被设计为以多种方式(特别是cfscript)复制 JavaScript,但存在一个细微的偏差,我还没有看到记录。对于函数(脚本和标签),JavaScript 使用提升,而 ColdFusion 则不使用。

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

在 CF9 之前,必须在函数顶部使用 var 关键字,从本质上消除了提升的需要。这与 JavaScript 不同,在 JavaScript 中 var 可以在函数中的任何位置使用并使用提升。CF9 ColdFusion 采用了随处声明的理念,但忽略了提升的实现。

在下面的两个示例中,JavaScript 仅处理单个作用域,并且x是函数局部的。

<!--- sets variables in 1 scope --->
<cfscript>
    var x = 0;
    x = 1;
    local.x = 7;
</cfscript>
Run Code Online (Sandbox Code Playgroud)

相比:

<!--- sets variables in 2 scopes --->
<cfscript>
    x = 1;
    var x = 0;
    local.x = 7;
</cfscript>
Run Code Online (Sandbox Code Playgroud)

为了避免由于缺乏提升而造成的潜在陷阱,您可以只var在函数的顶部,或者像 CF9 之前的那样做一些事情,在函数的顶部声明一个 var 结构,并用它作为所有变量的前缀(记住不要将其命名为本地)。例如

<cfset var localVars = StructNew()>
<cfset localVars.x = 7>
<cfset localVars.y = 1>
Run Code Online (Sandbox Code Playgroud)

varlocal

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

更多的

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