跳轉至內容

活動伺服器頁面/函式和子程式

來自 Wikibooks,開放書籍,開放世界
上一頁:條件和迴圈 索引 下一頁:使用 ADO 訪問資料庫

在本章中,我們將介紹建立過程的概念。過程可以是函式或子程式定義。一旦定義,過程可以從程式碼中的任何地方呼叫。閱讀完本章後,您應該瞭解如何編寫自己的過程,然後從程式碼中的其他地方呼叫該過程。

活動伺服器頁面的最強大功能之一是能夠建立強大的過程,這些過程可以擴充套件指令碼語言。如果您不熟悉過程程式設計,您可以將過程視為執行復雜任務的宏。一旦定義幷包含在您的網頁中,您就可以在您定義的任何程式碼塊中引用宏。

例如,如果您建立了一個函式來顯示當前日期和時間,您可以使用類似以下內容呼叫此函式

 <%= 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關鍵字。

當然,您可以在多個引數列表中混合使用ByRefByVal字首。您的列表中的每個引數都可以有一個修改它的字首(或者根本沒有)。

呼叫約定

[編輯 | 編輯原始碼]

呼叫子程式或函式時,您需要了解每個子程式或函式使用的不同調用約定。如果您沒有遵循這些準則,您在瀏覽器中載入頁面時將收到語法錯誤。

呼叫函式始終需要將引數括在括號中。與子程式不同,函式可以在表示式中使用,也可以作為另一個函式的引數使用。

 ' 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 & "&lt;br&gt;"
 	Next
 End Sub

變數I的範圍嚴格限制在過程宣告內。您將無法在過程之外引用I。它在函式被呼叫時建立,在函式完成時消失。

Active Server Pages 允許您在過程中引用全域性變數。雖然不建議這樣做,因為它會使您的過程可移植性降低,但在某些情況下這確實有用。

變數隱藏

[編輯 | 編輯原始碼]

可以使用相同的名稱在“區域性”和“全域性”宣告變數。這在 ASP 中是完全合法的。當您這樣做時,區域性變數將在過程的上下文中“隱藏”全域性變數。

 ' I declared globally and locally
 Dim I
 I = 5
 Response.Write "I = " & I & "&lt;br&gt;"
 CountTo 10
 Response.Write "I = " & I & "&lt;br&gt;"

 Sub CountTo(nFinal)
 	Dim I
 
 	For I = 1 To nFinal
 		Response.Write "count = " & I & "&lt;br&gt;"
 	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 的跨度呼叫該過程。該過程中的程式碼將識別這是用作初始化排序跨度引數的訊號。

您還應該意識到,上面概述的氣泡排序程式可以很容易地改寫為根本不使用遞迴。一個好的經驗法則是:如果使用遞迴可以更容易地編寫和維護過程,那麼就繼續使用它。

過程允許您建立可重複使用的程式碼“宏”來執行特定任務。函式是返回值的過程,而子程式不返回值。過程可以在頁面中的任何位置宣告,並從頁面中的任何位置引用。子程式或函式的輸出將放置在呼叫該過程的位置。

每個過程宣告可能包含一個可選的引數列表。引數可以宣告為“按引用”傳遞或“按值”傳遞。預設引數傳遞約定是“按引用”。使用ByValByRef顯式宣告過程引數。

複習問題

[編輯 | 編輯原始碼]
  • 過程可以是子程式或函式。
  • 只有函式可以返回值給呼叫者。
  • Function宣告函式,Sub宣告子程式。
  • 過程可以放置在呼叫它們的頁面的任何位置。
  • 常用的過程可以放在伺服器端包含檔案中。
  • 在過程名稱之後可以宣告一個可選的引數列表。
  • 引數列表必須用括號括起來。
  • 多個引數用逗號分隔。
  • 預設引數傳遞約定是“按引用”。
  • “按引用”傳遞給過程的 ASP 變數可能會被修改。
  • 預設引數傳遞約定是“按引用”。
  • 呼叫函式時,將引數括在括號中
  • 呼叫子程式時,不要在引數周圍使用括號
  • 函式可以在表示式中使用,也可以作為函式的引數使用
  • 區域性變數可以在過程塊內宣告。
  • 區域性變數會隱藏對全域性(頁面範圍)變數的引用。
  • 編寫一個函式來計算圓的直徑(2 * pi * 半徑),其中唯一的引數是半徑。
  • 編寫一個函式來計算矩形的面積(長度 * 寬度),它有兩個引數。
  • 編寫一個子程式來輸出問候語,例如:“早上好”、“下午好”或“晚上好”,具體取決於一天中的時間。
  • 將氣泡排序過程改寫為非遞迴函式。
上一頁:條件和迴圈 索引 下一頁:使用 ADO 訪問資料庫
華夏公益教科書