活动服务器页面/函数和子程序
在本章中,我们将介绍创建过程的概念。过程可以是函数或子程序定义。一旦定义,过程可以从代码中的任何地方调用。阅读完本章后,您应该了解如何编写自己的过程,然后从代码中的其他地方调用该过程。
活动服务器页面的最强大功能之一是能够创建强大的过程,这些过程可以扩展脚本语言。如果您不熟悉过程编程,您可以将过程视为执行复杂任务的宏。一旦定义并包含在您的网页中,您就可以在您定义的任何代码块中引用宏。
例如,如果您创建了一个函数来显示当前日期和时间,您可以使用类似以下内容调用此函数
<%= TodaysDate() %>
函数是一个将值返回给调用者的过程。通过“调用者”,我们的意思是您通过“函数调用”在 ASP 代码中调用过程的位置。函数使用Function语句声明,并以End Function语句结束。
' a totally useless function to get the current date and time
Function TodaysDate
TodaysDate = Now()
End Function
上面,我们有一个函数定义的示例,它将使用内置函数Now返回当前日期和时间。通过将值分配给与函数(TodaysDate)同名的变量,我们将值返回给调用过程。这里没有像 PHP 脚本语言或 C 编程中的return语句。
此函数可以在您的网页中的任何位置声明,它将可供您在页面中的任何位置调用。正如我们稍后将看到的那样,您甚至可以将常用的过程放在服务器端包含页面中,这样您就不必在网站的多个页面上编写重复的过程。
此函数完全没有用,因为直接调用内置函数Now更简单。
重要的是要注意,这样的过程声明在实际调用之前不会执行任何有意义的操作。它只是声明了一个过程,该过程将在调用时执行工作。
子程序是一个不返回值给调用者的过程。子程序使用Sub语句声明,并以End Sub语句结束。
' a subroutine to output today's date
Sub ShowDate
Response.Write("Today's Date is: ")
Response.Write(Now())
End Sub
上面的子程序调用将输出类似于“今天的日期是:2004 年 1 月 23 日 上午 06:43:12”的文本,当它被调用时。此文本在网页中的位置取决于子程序被调用的位置。您应该在您希望它输出文本的确切位置调用子程序。
与函数声明不同,您不允许将返回值分配给过程名称(ShowDate)。如果您尝试将子程序用作函数,您将收到运行时错误。同样地,当您尝试将函数用作子程序时,也会发生这种情况。
到目前为止,我们只研究了没有参数的过程。参数是一个 ASP 值,它被传递给过程以完成任务。可选参数列表与函数或子程序一起声明,方法是在括号中添加一个逗号分隔的参数名称列表。
' a subroutine to say hello
Sub SayHello(sName)
Response.Write "Hello "
Response.Write sName
Response.Write ", Nice to Meet You!"
End Sub
上面我们有一个非常简单的子程序,它接受一个参数。这个参数是要向谁打招呼的人的名字。当您使用类似于以下内容的调用调用此子程序时
<% SayHello("Bill Gates") %>
您的 ASP 页面将输出“你好,比尔·盖茨,很高兴认识你!”。在这种情况下,我们传递一个字符串字面量作为子程序的参数。您也可以传递一个 ASP 变量或一个复杂的表达式。
' a subroutine to convert hours/minutes to seconds
Function HoursToSeconds(nHours, nMinutes)
HoursToSeconds = nHours * 3600 + nMinutes * 60
End Function
最后,这个例子展示了如何为函数声明多个参数。这两个值用于计算从小时和分钟算起的秒数。您可以看到多个参数用逗号分隔。如果您想将一长串参数放在多行上,您将不得不使用“行延续”运算符,即下划线字符(_)。
默认情况下,所有过程参数都是按引用传递的。这意味着当您使用变量作为过程参数时,对过程内参数的任何更改都将反映在原始参数中。如果您想显式地将参数声明为“按引用传递”,您可以使用ByRef关键字
' explicitly pass by reference
Sub HelloInformal(ByRef sFullName)
If InStr(1, sFullName, " ") > 0 Then
sFullName = Left(sFullName, InStr(1, sFullName, " ")-1)
End If
Response.Write "Hello "
Response.Write(sFullName)
End Sub
在编写和调用这样的过程时,您需要小心。此过程实际上会修改调用参数,方法是删除除第一个单词以外的所有内容。
按引用传递是默认调用方法。因此,即使ByRef关键字不存在,参数也默认定义为“按引用传递”。请记住这一点,这样您就不会陷入上面提到的陷阱。
将值传递给过程的另一种方式是“按值传递”。这意味着在调用过程时,将取值的副本。参数保存值的副本,以便对该参数的任何修改都不会影响调用过程使用的任何参数。
应“按值传递”的参数应使用ByVal前缀声明。默认情况下,所有参数都是“按引用传递”。如果您需要按值传递一个值,您需要在参数声明前加上ByVal。
' explicitly pass by value
Sub HelloInformal(ByVal sFullName)
If InStr(1, sFullName, " ") > 0 Then
sFullName = Left(sFullName, InStr(1, sFullName, " ")-1)
End If
Response.Write "Hello "
Response.Write(sFullName)
End Sub
当调用此子程序调用时,您将保证原始参数不会被修改。使用之前声明的声明,您将没有相同的保证,该声明将参数声明为ByRef。
您应该只在绝对必要时使用ByVal。按引用传递参数比按值传递参数更有效。在典型使用中,大多数参数用作“只读”,因此您可能只会偶尔使用ByVal关键字。
当然,您可以在多个参数列表中混合使用ByRef和ByVal前缀。您的列表中的每个参数都可以有一个修改它的前缀(或者根本没有)。
调用子程序或函数时,您需要了解每个子程序或函数使用的不同调用约定。如果您没有遵循这些准则,您在浏览器中加载页面时将收到语法错误。
调用函数始终需要将参数括在括号中。与子程序不同,函数可以在表达式中使用,也可以作为另一个函数的参数使用。
' call a function
nSeconds = ToSeconds(2, 35)
' use a function call within an expression
nSeconds = 3600 + ToSeconds(2, 35)
' use a function as an argument to another function
sTime = MyFormatTime(ToSeconds(2, 35))
' ignore the return value for a function call
ToSeconds(2, 35)
在上面的最后一个示例中,您会注意到我们调用了函数,但忽略了返回值。只有当您的函数对全局变量有副作用或修改以“按引用”传递的变量时,这才是有用的。
另一方面,调用子程序需要您不要将参数括在括号中。此规则的唯一例外是当您在函数调用之前使用Call关键字时。下面显示了两种不同的调用方式
' call a subroutine (parentheses forbidden...)
SayHello "Bill Gates"
' parentheses allowed for subroutine call using "Call"
Call SayHello("Bill Gates")
就像您可以在网页中使用Dim语句声明变量一样,您也可以声明用于过程中的变量。这些通常被称为“局部变量”,因为它们只在过程声明中“局部”可用。在过程之外声明的变量可以被认为是“全局变量”。
您可以在过程块中声明局部变量,就像声明普通变量一样
' example of local variables as loop iterator
Sub CountTo(nFinal)
Dim I
For I = 1 To nFinal
Response.Write "count = " & I & "<br>"
Next
End Sub
变量I的范围严格限制在过程声明内。您将无法在过程之外引用I。它在函数被调用时创建,在函数完成时消失。
Active Server Pages 允许您在过程中引用全局变量。虽然不建议这样做,因为它会使您的过程可移植性降低,但在某些情况下这确实有用。
可以使用相同的名称在“局部”和“全局”声明变量。这在 ASP 中是完全合法的。当您这样做时,局部变量将在过程的上下文中“隐藏”全局变量。
' I declared globally and locally
Dim I
I = 5
Response.Write "I = " & I & "<br>"
CountTo 10
Response.Write "I = " & I & "<br>"
Sub CountTo(nFinal)
Dim I
For I = 1 To nFinal
Response.Write "count = " & I & "<br>"
Next
End Sub
在上面的示例中,我们有一个全局声明的变量I(实际上范围是网页的生命周期)和局部声明的变量。当您运行此示例代码时,您会注意到全局变量 I 在调用CountTo之后保持不变。这是因为局部变量与全局变量完全分开。对局部变量的更改不会影响全局I。
由于您声明了一个与全局变量同名的局部变量,因此您实际上隐藏了对全局变量的引用。在过程CountTo中,无法引用全局I。虽然其他语言提供了访问此类变量的机制,但 ASP 没有。解决此问题的一种方法是:更改局部变量的名称或将全局变量作为参数传递(使用不同的名称)。
递归指的是让函数“调用自身”以执行重复任务的做法。创建递归函数就像声明一个包含对自身调用的函数一样简单。
' A simple bubble-sort routine
Sub BubbleSort(arrList, nSpan)
Dim I, vTemp
If nSpan = 0 Then nSpan = UBound(arrList)
For I = 0 To UBound(arrList) - nSpan
If arrList(I) > arrList(I + nSpan) Then
vTemp = arrList(I + nSpan)
arrList(I + nSpan) = arrList(I)
arrList(I) = vTemp
End If
Next
If nSpan > 1 Then BubbleSort arrList, nSpan-1
End Sub
正如您在子程序的最后一行看到的那样,冒泡排序程序会随着跨度每次减少一个而递归调用自身。当跨度距离达到 1 时,函数将停止调用自身。“递归深度”或“递归级别”取决于传递的数组的大小。如果您有七个元素,则该过程将调用自身 6 次。
下面是一些 ASP 代码,它将通过创建一个包含七个数字的数组并调用BubbleSort过程来测试冒泡排序程序
Response.Write "<p>"
Dim arrTest, I
arrTest = Array(8753, 5234, 434, 5234, 34, 1, 65, 123)
For I = 0 To UBound(arrTest)
Response.Write "arr("&I&") = " & arrTest(I) & "<br>"
Next
BubbleSort arrTest, 0
Response.Write "</p>"
Response.Write "<p>"
For I = 0 To UBound(arrTest)
Response.Write "arr("&I&") = " & arrTest(I) & "<br>"
Next
Response.Write "</p>"
您应该注意,我们首先用 0 的跨度调用该过程。该过程中的代码将识别这是用作初始化排序跨度参数的信号。
您还应该意识到,上面概述的冒泡排序程序可以很容易地改写为根本不使用递归。一个好的经验法则是:如果使用递归可以更容易地编写和维护过程,那么就继续使用它。
过程允许您创建可重复使用的代码“宏”来执行特定任务。函数是返回值的过程,而子程序不返回值。过程可以在页面中的任何位置声明,并从页面中的任何位置引用。子程序或函数的输出将放置在调用该过程的位置。
每个过程声明可能包含一个可选的参数列表。参数可以声明为“按引用”传递或“按值”传递。默认参数传递约定是“按引用”。使用ByVal或ByRef显式声明过程参数。
- 过程可以是子程序或函数。
- 只有函数可以返回值给调用者。
- Function声明函数,Sub声明子程序。
- 过程可以放置在调用它们的页面的任何位置。
- 常用的过程可以放在服务器端包含文件中。
- 在过程名称之后可以声明一个可选的参数列表。
- 参数列表必须用括号括起来。
- 多个参数用逗号分隔。
- 默认参数传递约定是“按引用”。
- “按引用”传递给过程的 ASP 变量可能会被修改。
- 默认参数传递约定是“按引用”。
- 调用函数时,将参数括在括号中
- 调用子程序时,不要在参数周围使用括号
- 函数可以在表达式中使用,也可以作为函数的参数使用
- 局部变量可以在过程块内声明。
- 局部变量会隐藏对全局(页面范围)变量的引用。
- 编写一个函数来计算圆的直径(2 * pi * 半径),其中唯一的参数是半径。
- 编写一个函数来计算矩形的面积(长度 * 宽度),它有两个参数。
- 编写一个子程序来输出问候语,例如:“早上好”、“下午好”或“晚上好”,具体取决于一天中的时间。
- 将冒泡排序过程改写为非递归函数。