活動伺服器頁面/函式和子程式
在本章中,我們將介紹建立過程的概念。過程可以是函式或子程式定義。一旦定義,過程可以從程式碼中的任何地方呼叫。閱讀完本章後,您應該瞭解如何編寫自己的過程,然後從程式碼中的其他地方呼叫該過程。
活動伺服器頁面的最強大功能之一是能夠建立強大的過程,這些過程可以擴充套件指令碼語言。如果您不熟悉過程程式設計,您可以將過程視為執行復雜任務的宏。一旦定義幷包含在您的網頁中,您就可以在您定義的任何程式碼塊中引用宏。
例如,如果您建立了一個函式來顯示當前日期和時間,您可以使用類似以下內容呼叫此函式
<%= 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 * 半徑),其中唯一的引數是半徑。
- 編寫一個函式來計算矩形的面積(長度 * 寬度),它有兩個引數。
- 編寫一個子程式來輸出問候語,例如:“早上好”、“下午好”或“晚上好”,具體取決於一天中的時間。
- 將氣泡排序過程改寫為非遞迴函式。