ch7 (14)



Day 7 -- Building a Home for Your Code




DAY 7
Building a Home for Your Code




CONTENTS


Subroutines

Declaring a Subroutine
Calling a Subroutine
Exiting a Subroutine

Functions

Declaring a Function
Calling a Function
Exiting a Function

Passing Arguments into Procedures
Why Are Procedures Useful?
Event Procedures
Method Procedures
Procedures, Subroutines, Functions, Events, and Methods!
Where to Put Procedures
Summary
Q&A
Workshop
Quiz



On this, the last day of the first week, you will learn one more
fundamental concept: how VBScript code is organized. VBScript,
like most other languages, stores code in what we call procedures.
As you will see, you can design procedures in different ways
for different purposes. Procedures are containers for groups of
code that accomplish specific tasks. Procedures can call and be
called by other procedures, giving the programmer a great deal
of flexibility. Today, you will learn how to create and use procedures,
as well as the benefits of using them.

A procedure is a grouping of code statements that can be
called by an associated name to accomplish a specific task or
purpose in a program. Once a procedure is defined, it can be called
from different locations in the program as needed.

Procedures come in two varieties: subroutines and functions.
Each have special characteristics that set them apart, and today's
lesson considers both subroutines and functions individually.
Having been introduced to subroutines and functions, you will
learn about a type of procedure called an event. Events
respond to user-initiated activities, such as clicking a button.
Another type of procedure, called a method, performs a
specific task of an object. You will learn how all these procedures
work together as well as where you should place them in an HTML
document.
Subroutines


The first type of procedure I will discuss is the subroutine.
A subroutine is a container that holds a series of VBScript
statements. Suppose you'd like to create a block of code that
accomplishes some specific task. Maybe you need to accomplish
that task in various places throughout your code. All you need
to do is create, or declare, the subroutine in your script. Once
you've declared the subroutine, you can call it anywhere within
your code. When your program calls a subroutine, the flow
of the code is temporarily diverted to the statements within the
subroutine. Once the subroutine has finished executing, control
returns to the code that called the subroutine and execution picks
up from there.

A subroutine is a block of code that can be called from
anywhere in a program to accomplish a specific task. Subroutines
can accept starting data through subroutine declaration variables
called parameters. However, subroutines do not automatically return
a result code or an argument to the caller.
Declaring a Subroutine


You declare subroutines using the Sub
keyword and end them using the End
Sub statement. The structure of a subroutine is

Sub Subroutine_Name(argument1,
argument2, ..., argumentn)
   ...code within the subroutine
End Sub


where Subroutine_Name
is the name of the subroutine and argument1
through argumentn
are optional arguments, often called parameters, that
you can pass to the subroutine. If you choose not to pass any
arguments to the subroutine, the parentheses are optional, as
you will see in a moment.

An argument or a parameter is a variable that a
procedure requires in order to execute. In VBScript, arguments
must be supplied in the order specified by the procedure.

The name of a subroutine should adequately describe what the subroutine
is for. Make the name as descriptive as you can, and name the
subroutine in plain English rather than in some cryptic abbreviations
only you understand and might even forget a week later. You must
name subroutines using the same rules as variables; letters and
numbers are fine as long as the first character is not a number,
and you cannot use symbols. If you enter an invalid name for a
subroutine, the VBScript run-time interpreter will alert you of
the problem when you attempt to try your script. The following
are some valid subroutine names:

WelcomeTheUser
PrintInvoice
Meters2Yards


The following are some unacceptable subroutine names:

User.Welcome
2Printer
Miles*1.609


If you want, a subroutine can require that the code statement
that calls that subroutine provide one or more arguments
or variables that the subroutine can work with. Any time you need
preexisting data to perform the task within the subroutine, arguments
are very helpful. For example, the following subroutine accepts
an argument to be used in the subroutine as a message to be displayed
to the user:

Sub ShowMessage(CurrentMessage)
   MsgBox CurrentMessage, vbOkOnly,
"Important Message"
End Sub


In this case, CurrentMessage
is the argument, and it is treated like any other variable
in the subroutine. It is treated exactly as if it had been declared
with a Dim statement with
one very important difference. The CurrentMessage
argument variable starts out pre-initialized with a value that
was supplied by the code that called this subroutine. When you
declare a procedure and specify that it will receive arguments,
you can specify how it interacts with those argument variables.
You can either give the subroutine a copy of a value supplied
as an argument, or you can refer it to the actual variable that
the caller is using. If you give the subroutine a copy of
the variable, the subroutine can then make changes to its own
copy without changing the original. If, on the other hand, the
subroutine simply references the variable owned by the caller,
it cannot change the variable-it doesn't own it. I'll discuss
this in more detail later in today's lesson.

Often, a subroutine might not require any arguments, and you can
drop the parentheses. Suppose you have a subroutine that simply
displays information to the user. In that case, the subroutine
doesn't need any arguments, as in the following case:

Sub ShowAboutMessage
   MsgBox "This Web page was designed
by the WebWizard."
End Sub


In this case, you can see that the code lists no arguments, so
it does not require the parentheses. On the other hand, if you
declared a procedure using one or more arguments, you'd use the
parentheses:

Sub ShowAboutMessage(Message)
   MsgBox Message
End Sub

Calling a Subroutine

Now that you've learned how to create a subroutine, how do you
call one? You can call a subroutine throughout the rest of the
application once you've declared and created it. You can call
subroutines by using the Call
keyword or just entering the name of the subroutine on a line
of code. For example, to call a subroutine called ShowMessage,
you could enter

ShowMessage "This is the message."


You could also use the Call
keyword and enter

Call ShowMessage("This is the message.")


Notice that in the first method, you do not place parentheses
around the arguments of the subroutine. On the other hand, if
you use Call, you must enclose
the arguments in parentheses. This is simply a convention that
VBScript requires. What if a subroutine has no arguments? To call
the subroutine ShowAboutMessage,
you could enter

ShowAboutMessage


or

Call ShowAboutMessage()


or you could use

Call ShowAboutMessage


The first method simply lists the name of the subroutine. The
second method uses Call
but doesn't require parentheses because the subroutine has
no arguments. Whether you use the parentheses when you call or
declare a subroutine with no arguments is a personal preference
about writing code. When you call a subroutine without the Call
statement, it can be more difficult to figure out the difference
between a subroutine and a variable in your code, especially if
your code is lengthy. Although the choice is up to you, it is
generally recommended that you always use the Call
statement when calling subroutines for the sake of readability.
Exiting a Subroutine

The code within your subroutine will execute until one of two
things happens. First, the subroutine might get down to the last
line, the End Sub
line, which terminates the subroutine and passes the baton
back to the caller. This statement can appear only once at the
end of the subroutine declaration. The second possibility is that
VBScript could execute the following code statement:

Exit Sub


when placed inside the subroutine. You might use this statement
if you need to provide more than one exit point for the subroutine.
However, you shouldn't need to use this very often if your subroutine
is constructed properly. Consider the following subroutine:

Sub ConvertFeetToInches
   Feet = InputBox("How many feet
are there?")
   If Feet < 0 Then
      Exit Sub
   Else
      MsgBox "This equals "
& Feet * 12 & " inches."
   End If
End Sub


This subroutine contains an Exit Sub
statement, which could be avoided by changing the subroutine to

Sub ConvertFeetToInches
   Feet = InputBox "How many feet
are there?")
   If Feet >= 0 Then
      MsgBox "This equals "
& Feet * 12 & " inches."
   End If
End Sub


Here, the If…Then conditional
structure was rewritten to avoid the need for the Exit
Sub statement. In this case, the subroutine
could be changed to avoid Exit Sub.
Keep in mind, however, that you may occasionally need to use Exit
Sub to exit a subroutine, particularly when handling
errors.



Note


For more information on how to handle errors, refer to Day 17, "Exterminating Bugs from Your Script."





Functions


The second type of procedure is called a function. Like
a subroutine, a function also holds a series of VBScript statements.
The only difference is that a function actually returns a value
to the code statement that called it.

A function is a block of code that can be called from anywhere
in a program to accomplish a specific task. Functions can accept
parameters and can also return a result code to the caller.

You've seen in earlier lessons how you can fill a variable with
a value supplied on the right side of an assignment statement:

ZipCode = 49428


In the same manner, you can fill a variable with a value supplied
by a function you define:

ZipCode = GetZipCode("Jenison")


As with the subroutine, the flow of code is redirected to the
function while the code within the function executes. Once the
function has finished executing, control returns back to the code
that called the function, the value from the function is assigned
to the calling code, and execution picks up from there.
Declaring a Function

To declare a function, use the Function
keyword instead of the Sub
keyword. You end functions using the End
Function statement. The structure of a function
is

Function Function_Name(argument1,
argument2, …, argumentn)
     ...code within the function

End Sub


where Function_Name
is the name of the function and argument1
through argumentn
are optional arguments you can pass to the function. As with
the subroutine, the parentheses are not required if no arguments
are passed to the function.

As before, make sure the name of the function adequately describes
what the function does. The same naming conventions that apply
to the subroutine also apply to the function. Also, arguments
are passed to the function the same way they are passed to a subroutine.
For example, if you had a function named GetMiles
with an argument variable used to pass the number of kilometers
into the function, your declaration would look like this:

Function GetMiles(Kilometers)
   GetMiles = Kilometers * 1.609
End Function


In this example, the function returns the number of miles. Notice
that it looks like the programmer has accidentally used the same
variable name as the function in storing the number of miles.
In reality, there's nothing accidental about this. In order to
pass a value back from a function, you must set a special function
"variable" that is named after the function. VBScript
automatically knows that your intention is to pass the value back
to the caller, not to create a temporary variable within the function.
If you don't set this "variable" in your program, the
result will be a zero or an empty string. It's very important,
therefore, that you make sure your function returns some valid
value. This value should be assigned to the function name itself
within the block of code that makes up the function.

It's very easy to get busy coding within a function, creating
variables as you need them, and forget to store the result in
the return "variable" for the function. For example,
you might have a function like what's shown in Listing 7.1.


Listing 7.1. A function that doesn't return a result.



Function GetAge
   Dim Age
   Dim Valid
   Do
      Age = InputBox "Please
enter your age ( 5 - 120, please): "
      If Age >= 5 and Age <=
120 Then
         Valid =
vbTrue
      Else
         MsgBox "The
age you have entered is invalid. Please enter it again."

      End If
   Loop While Valid = vbFalse
End Function



This function contains a loop that asks the user for his age and
continues to ask the user until the age falls within the correct
range. Once it does, the condition of the control structure is
no longer met and the loop completes. The programmer was so happy
she got the function working that she forgot one thing-to pass
the age back to the caller! One very important line at the end
was omitted.

The function in Listing 7.1 should be written as shown in Listing
7.2.


Listing 7.2. The same function, but now it does return a result.




Function GetAge
   Dim Age
   Dim Valid
   Do
      Age = InputBox "Please
enter your age ( 5 - 120 ): "
      If Age >= 5 and Age <=
120 Then
         Valid =
vbTrue
      Else
         MsgBox "The
age you have entered is invalid. Please enter it again."

      End If
   Loop While Valid = vbFalse
   GetAge = Age
End Function



Here, you can see that the programmer set the function variable
equal to the variable Age
declared inside the function. Then the programmer assigned that
variable to the function, effectively returning it to the caller.

The programmer could have also written the function without the
temporary age variable, as shown in Listing 7.3.


Listing 7.3. The same function without a temporary variable
that stores the return result.



Function GetAge
   Dim Valid
   Do
      GetAge = InputBox "Please
enter your age ( 5 - 120 ): "
      If GetAge >= 5 and GetAge
<= 120 Then
         Valid =
vbTrue
      Else
         MsgBox "The
age you have entered is invalid. Please enter it again."

      End If
   Loop While Valid = vbFalse
End Function



Even though this implementation would work just fine, it's a good
programming practice to use a temporary variable and then make
the assignment at the end. This is useful in part because it's
easier to read the code. The reader can make better sense of your
code when every variable is declared and the function uses no
variables that do not have declarations. Because the function
"variable" has no formal declaration and could potentially
be set in several different code branches within the function,
it might confuse the surveyor of your code if your code is lengthy.

Another important point to keep in mind is that when you write
a function that uses arguments, you must not declare variables
with the same name as the arguments using the Dim
statement. When you specify arguments in a function, those arguments
are automatically declared as variables in the function, but they
are set to whatever was supplied when the procedure is called.
If you try to declare them again, you will get an error because
they're already in use in the function. For instance, this function
would result in an error:

Function ConvertToSeconds(OrigHours,
OrigMinutes, OrigSeconds)
   Dim OrigSeconds
   OrigSeconds = OrigHours * 3600 + OrigMinutes
* 60 + OrigSeconds
   ConvertToSeconds = OrigSeconds
End Function


The function would be incorrect because the variable OrigSeconds
is already part of the argument list. Rather, you could assign
the result directly to the function output variable or use some
other variable:

Function ConvertToSeconds(OrigHours,
OrigMinutes, OrigSeconds)
   Dim Total
   Total = OrigHours * 3600 + OrigMinutes * 60
+ OrigSeconds
   ConvertToSeconds = Total
End Function

Calling a Function

Now that you've seen how to declare a function, you need to know
how to call it. The benefit of using a function is that you can
pass back a piece of data to the caller. The subroutine does not
enable you to do this because it does not return anything. You
will see a way to change variables in the calling code with a
subroutine later today, but the function is a better way to get
data back and forth. To call a function, you simply use the syntax

return_variable =
function_name(argument1, argument2, …, argumentn)


Notice that in this case, the syntax is quite a bit different
from the subroutine. Here, you can assign the function to a variable
(or another expression that can be updated with a value, such
as a property, which will be covered in later lessons), or you
needn't assign it to anything. The parentheses are optional only
when no arguments are passed to the function.

For an example of its use, suppose you have a function called
GetAge. To use the GetAge
function, you could enter the statement

UserAge = GetAge()


or

UserAge = GetAge


Notice that this function doesn't need any arguments, and the
result is assigned to a variable named UserAge.
The following function requires three arguments-hours, minutes,
and seconds-and returns the number of seconds:

Function GetSeconds(Hrs, Min, Sec)
   GetSeconds = Hrs * 3600 + Min *
60 + Sec
End Function





Note
The terms arguments and parameters are usually used synonymously.




You could then call this function using a statement like

NumSeconds = GetSeconds(2, 34, 25)


or

NumSeconds = GetSeconds 2, 34, 25


where the total number of seconds is returned to the variable
NumSeconds.




Note


Make sure you never create variables that have the same name as keywords already used by VBScript. These keywords are called reserved words and include terms such as Date, Minute, Second, Time, and so on. For a complete list of reserved words, refer to Appendix A, "VBScript Syntax Quick Reference."






The statement

Call GetSeconds(2, 34, 25)


would also be valid, but it wouldn't be very useful because you're
not retrieving the number of seconds from the function! This simply
calls a function as if it were a subroutine, without handling
the return value. You can also utilize a function within an expression,
such as

MsgBox "There are " & GetSeconds(2,
34, 25) & _
" seconds in the time you have entered."


In this case, you must use parentheses around your arguments.
You don't need to assign a variable to the return of the function
because the return value is automatically used within the statement.
Although this is certainly legal, it is not always the best programming
practice. If you want to use the result of the function more than
once, you must store the result in a variable. Otherwise, you
will have to call the function again and waste the computer's
resources in doing the calculation all over again. Likewise, storing
the value in a variable to avoid repeated calls makes the code
more readable and maintainable.
Exiting a Function

To exit a function, you use the same method as when you exit a
subroutine, namely the End Function
statement. This statement can only appear once at the end
of the function declaration. You have seen this statement used
in the functions discussed so far. You can also use the statement
Exit Function to break
out of a function just like you used the Exit
Sub statement to exit a subroutine. As before,
it's better to exit a function naturally when your code reaches
the final End Function statement
than to use an Exit Function
line of code to terminate the function in the middle of the statements.
The code is simply easier to follow when you avoid such forced
exit statements.
Passing
Arguments into Procedures

Earlier today, you saw the two ways you can pass arguments into
a procedure. The manner you use is determined by the way you declare
the procedure. Based on that declaration, when a procedure is
called, you either provide a copy of a variable to a procedure
so that it has a local copy to modify if necessary, or you refer
the procedure to the original variable itself. Either way,
the variable owned by the caller cannot be modified by the procedure.
If you refer the procedure to the original variable, you are in
effect supplying the memory address of that variable to the procedure.
However, VBScript hides the memory address details from you. From
the programmer's perspective, you are simply providing the variable
name to the procedure. But VBScript does not allow you to change
the variable-the interpreter will trigger an error when the Web
page loads if you try to do so.


Note


The current VBScript interpreter in Internet Explorer does not support the ByRef parameter familiar to many Visual Basic programmers. In Visual Basic 4.0, for example, if you use a ByRef parameter, you can make changes to that variable within the subroutine that defined the parameter and the changes are reflected back to the variable supplied in that parameter position by the calling code. This is a
capability that one might expect to be introduced in subsequent releases. You might wish to check the current documentation to ascertain the current implementation level if you are interested in this feature. You can refer to Microsoft's online VBScript
documentation at www.microsoft.com/vbscript and look up Sub and call under the language reference search. Alternatively, you can check to see if
there is an update sheet for this book at Macmillan's site (www.mcp.com), or at the authors' site (www.doubleblaze.com/vbs21day).






To illustrate this concept, suppose your secretary wishes to use
a letter you sent to a client as a template for creating another
letter. You don't mind sharing the letter with her, but you must
not allow the original to be changed in any way. You could make
a copy of your letter and give it to her, after which she could
mark it up if she wished and make changes to it. In this case,
she would not change the original because it would be in your
hands. Or you could simply show her the original and only allow
her to examine it without changing it. In that case, she wouldn't
get her own copy, but she could use the contents of the letter
to make her own letter if she wished, as long as she didn't change
the original.

Giving your secretary a copy of the letter can be likened to passing
a variable into a procedure by value. You give the procedure
a copy of the variable that is totally divorced from its original.
To do this, you simply place the keyword ByVal
in front of the variable in the declaration of the procedure.

Passing by value means that a copy of the original value
is given to the procedure. The procedure can change its own copy,
but it won't affect the original owned by the caller.

To refer the procedure to the original without giving it a copy
of the variable, you omit the ByVal
keyword. This is the default case and is called passing a variable
by reference. If you try to modify a variable passed to
a procedure by reference, VBScript will give you an error message
when the Web page loads into the browser. The procedure can read
the contents of a variable passed in by reference, but it cannot
change its contents because the procedure doesn't own the variable.

Passing by reference means that the procedure is allowed
to read the variable owned by the caller. The procedure is not
allowed to change the value of the variable because it is not
the owner of that variable.

If you wish to pass variables into a procedure by value, you simply
declare the procedure using the format

Sub Subroutine_Name(ByVal argument1,
ByVal argument2, ... ByVal argumentn)


when creating a subroutine and

Function Function_Name(ByVal argument1,
ByVal argument2, ... ByVal argumentn)


when creating a function. To pass variables by reference, you
simply omit the ByVal keyword
as shown here for a subroutine:

Sub Subroutine_Name(argument1,
argument2, ... argumentn)


and here for a function:

Function Function_Name(argument1,
argument2, ... argumentn)


As an example of passing a variable by value, consider the sample
Web page shown in Fig-ure 7.1.

This Web page is named byval.htm
and is located on the CD-ROM that comes with the book. This Web
page contains a text box where the user can enter a number. That
number is then passed into a subroutine, which takes the value
and doubles it, displaying it to the user. Then, when the subroutine
has finished, the caller resumes by displaying the value of the
variable it passed in. The code for this Web page is shown in
Listing 7.4.


Listing 7.4. Passing variables to a procedure by value.



<SCRIPT LANGUAGE="VBScript">

<!-- Option Explicit

   Sub cmdDouble_OnClick()

      Dim Number
      Dim Doubled

      Number = txtBefore.Value
      Doubled = Doubler(Number)


      txtAfter.Value = Number
      txtNewValue.Value = Doubled


   End Sub

   Function Doubler(ByVal Number)
      Number = Number * 2
      Doubler = Number
   End Function

-->
</SCRIPT>



As you can see in Figure 7.1, the number remains unchanged after
the Doubler function is called.
The number returned by the function is indeed double that of the
one passed to it. Since the calling subroutine passed the variable
by value, the function could modify its own copy of the variable.

Figure 7.1 : A Web page that doubles a value entered by the user.

What would have happened if the variable had been passed by reference?
Figure 7.2, which displays the Web page named badbyval.htm,
shows the result.

Figure 7.2 : A Web page with a procedure that tries to modify a variable passed to it by reference.

As you can see, an error results. Listing 7.5 shows the incorrect
code.


Listing 7.5. Incorrectly trying to modify a variable passed
by reference.



<SCRIPT LANGUAGE="VBScript">

<!-- Option Explicit

   Sub cmdTest_OnClick()

      Dim Number
      Dim Doubled

      Number = txtBefore.Value
      Doubled = Doubler(Number)


      txtAfter.Value = Number
      txtNewValue.Value = Doubled


   End Sub

   Function Doubler(Number)
   ' This code is incorrect for the sake of this
example, as described above!
      Number = Number * 2
      Doubler = Number
   End Function

-->
</SCRIPT>



As you can see, the function now gets the value by reference.
When it tries to change the variable, VBScript flags the error
that appears in Figure 7.2. It is your responsibility as a programmer
to make sure you pass variables into procedures properly to avoid
errors such as these.

Follow these rules of thumb to stay on safe ground:

Never change a variable passed to a procedure by reference,
or you will get an error. If you wish to change the variable within
the procedure, pass it by value.
Never expect to change a variable in the caller when passed
to a procedure by value. You're only giving the procedure a copy
of the variable, not the original.


You do not have to pass every variable into the procedure
in the same way. You can pass some variables in by value and some
by reference, as in the following example:

Function ConvertToMeters(ByVal Inches,
Feet)
      Inches = Feet
* 12 + Inches
      ConvertToMeters = Inches *
0.0254
   End Function


Here, the variable Inches
is passed in by value because the programmer designed the function
to change the value of this variable. In order to accomplish this,
he must declare the variable Inches
by value so that the function gets its own copy and can modify
it. The variable Feet, on
the other hand, never gets changed, so it can be passed by reference.

Figure 7.3 shows a working implementation of this function in
the Web page named meters.htm.

Figure 7.3 : The metric converter Web page using ByVal.

The code listing for this Web page is shown in Listing 7.6.


Listing 7.6. Passing variables to a procedure by value.



<HTML>

<HEAD>
<TITLE>The Metric Converter</TITLE>
</HEAD>

<BODY>

<H1><A HREF="http://www.mcp.com"><IMG  ALIGN=BOTTOM

SRC="../shared/jpg/samsnet.jpg" BORDER=2></A>

The Metric Converter</H1>

<HR>

<CENTER><H2>Passing variables both by value and by

reference into a procedure</H2>

<P>Enter a distance in inches and feet and click on the
"Convert" button.
The result will be displayed in meters.

<PRE><INPUT NAME="txtFeet" SIZE=10 > feet    

<INPUT NAME="txtInches" SIZE=10 > inches</PRE>

<P><INPUT TYPE="BUTTON" NAME="cmdConvert"
VALUE="Convert">
<P><INPUT NAME="txtResult" SIZE=50 ></CENTER>


<HR>

<center>
from <em>Teach Yourself VBScript in 21 Days</em> by

<A HREF="../shared/keith.htm">Keith Brophy</A>
and
<A HREF="../shared/tim.htm">Tim Koets</A><br>

Return to <a href="..\default.htm">Content Overview</A><br>

Copyright 1996 by SamsNet<br>
</center>

<SCRIPT LANGUAGE="VBScript">
<!--  Option Explicit

   Sub cmdConvert_OnClick()
   
     Dim Inches, Feet, Meters

     Inches = txtInches.Value
     Feet = txtFeet.Value

     Meters = ConvertToMeters(Inches, Feet)


     txtResult.Value = "There are "
& Meters & " meters in " & Feet & _

                      
" feet and " & Inches & " inches."


   End Sub

   Function ConvertToMeters(ByVal Inches, Feet)


      Inches = Feet * 12 + Inches

      ConvertToMeters = Inches *
0.0254

   End Function

-->
</SCRIPT>

</BODY>

</HTML>



As you can see from Figure 7.2, the user simply enters the number
of inches and feet in the text boxes shown and clicks on the Convert
button. That calls the function ConvertToMeters,
which converts the values into meters and returns the value, which
is then displayed in the result text box on the Web page.

Because the variable Feet
never gets changed, you do not have to worry about passing it
by value. The variable Inches,on
the other hand, does get changed because the function takes the
number of feet and multiplies by 12 to convert to inches, adding
that result to the original number of inches. In order to perform
this operation, the function must have its own copy of the Inches
variable, which is why it must be passed to the function by value.

You can, of course, decide to always pass variables into your
procedures by value to avoid the possibility of introducing errors
into your programs. This is fine, as long as you realize that
the variables you modify will not change those found in the caller
of the procedure. You also have to go through the extra work of
placing a ByVal keyword in
front of every variable of your function declarations. On the
other hand, passing in values by reference is more efficient for
the VBScript interpreter because it doesn't have to clone a copy
of the variable for the procedure-it can simply refer the procedure
to the variable instead.
Why
Are Procedures Useful?

If you're new to programming, you might be wondering why procedures
are useful in the first place. Now that you've seen how to use
them, I'll discuss why they are so beneficial. There are three
primary reasons: readability, maintainability, and correctness.

Procedures are useful any time you have a task that must be accomplished
many times, perhaps in many places in your code, throughout your
program. Suppose you request an order number from the user, and
each time the number is entered, you want to make sure it's valid.
One option is to write code that checks each time an order is
entered as shown in Listing 7.7.


Listing 7.7. Code that should be placed in a function.



SpouseOrder = InputBox("What order
would you like for your spouse?")
If SpouseOrder < 0 Then
   MsgBox "The order number is invalid."

End If
YourOrder = InputBox("What order would you like for yourself?")

If YourOrder < 0 Then
   MsgBox "The order number is invalid."

End If
ChildOrder = InputBox("What order would you like for your
children?")
If ChildOrder < 0 Then
   MsgBox "The order number is invalid."

End If



As you can see from this example, the same check is repeated three
times in your code. This results in code that is not only more
difficult to read, but also more difficult to maintain.

Rather than type the same code three times, wouldn't it make your
code more readable if you created a function and placed the repeating
code within that function? Suppose you call the function VerifyOrderNumber
and place the common code in that function. Then, the code
might look like that in Listing 7.8.


Listing 7.8. Using a function to improve the program.



Sub GetOrders
Do
   SpouseOrder = InputBox("What order would
you like for your spouse?")
Loop Until VerifyOrderNumber(SpouseOrder) = True

Do
   YourOrder = InputBox("What order would
you like for yourself?")
Loop Until VerifyOrderNumber(YourOrder) = True

Do
   ChildOrder = InputBox("What order would
you like for your children?")
Loop Until VerifyOrderNumber(YourOrder)
End Sub
Function VerifyOrderNumber(OrderNumber)

   If OrderNumber < 0 Then
      MsgBox "The order number
is invalid."
      VerifyOrderNumber = False

   Else
      VerifyOrderNumber = True
   End If

End Function



If the user enters values greater than zero, the function returns
with a Boolean variable indicating the result is valid. Otherwise,
the function returns the Boolean value for false, and the loop
continues to prompt the user until an order number is entered,
calling the function again each time through.

In this case, you have placed all the repeating code within a
function so that the code within the function appears just once
rather than several times throughout the program. Doing this has
several advantages. First of all, it's a lot easier for the reader
of the code. He can see what the code does in one word rather
than having to wade through all the details. Furthermore, it cuts
down on the size of the code listing. Perhaps most important,
it makes the code more maintainable.


Note


To keep the example simple, the amount of repeating code you save in this sample code is relatively small. Keep in mind that many blocks of code which are candidates for procedures could be many times larger. When these code blocks are moved to procedures,
the advantages of "procedurized" code mentioned here become even greater. If you're an experienced computer programmer, you might realize that a computer has to do more work to call a procedure than to process statements that are simply in the
flow of code without a procedure call. However, this extra amount of work is very slight in the context of your overall script and will typically have no visible effect on the speed of your script. In other words, "procedurizing" your code has a
lot of advantages and virtually no disadvantages.





Suppose you realize that a valid order number not only should
be greater than zero, but that it also must be less than 5000.
If you wrote your program without the subroutine, as in Listing
7.8, you would have to look for every place in your program that
asks for an order number and modify the code after it to include
the additional check. Your code would look like that in Listing
7.9.


Listing 7.9. Code that is a pain to maintain because the repeatable
code is not contained within a function.



SpouseOrder = InputBox("What order
would you like to place for your spouse?")
If SpouseOrder < 0 and SpouseOrder >= 5000 Then

   MsgBox "The order number is invalid."

End If
YourOrder = InputBox("What order would you like to place
for yourself?")
If YourOrder < 0 and YourOrder >= 5000 Then
   MsgBox "The order number is invalid."

End If
ChildOrder = InputBox("What order would you like to place
for your children?")
If ChildOrder < 0 and ChildOrder >= 5000 Then
   MsgBox "The order number is invalid."

End If



In this case, you had to make three modifications to your program.
You're fortunate because all the changes are in one place; in
reality, they could spread all across the program! The better
solution is to make the change in one place, as shown in Listing
7.10. Note that the calling code remains identical to the procedure-calling
example shown earlier. It is only one statement in the body of
the function itself that must change.


Listing 7.10. Using a function to improve the program.



Do
   SpouseOrder = InputBox("What
order would you like for your spouse?")
Loop Until VerifyOrderNumber(SpouseOrder) = True

Do
   YourOrder = InputBox("What order would
you like for yourself?")
Loop Until VerifyOrderNumber(YourOrder) = True

Do
   ChildOrder = InputBox("What order would
you like for your children?")
Loop Until VerifyOrderNumber(YourOrder)



The function would be written in the following way:

Function VerifyOrderNumber(OrderNumber)


   If OrderNumber < 1 and OrderNumber
>= 5000 Then
      MsgBox "The order number
is invalid."
      VerifyOrderNumber = False

   Else
      VerifyOrderNumber = True
   End If

End Sub


Now, rather than make three changes, you simply have to make one.
It doesn't matter how many times you call the function or where
you make the calls in your code. Because all the functionality
is wrapped up in one location, you only have to change that location
itself.

Procedures help enhance a program in three ways. They make the
program more readable because there is less code and the surveyor
of the code isn't always forced to see the details of every part
of the code. The program is also more maintainable because changing
code in a function requires one change in only one place instead
of many changes scattered throughout the application. Finally,
your code is more correct because the likelihood of making a mistake
when you're duplicating code across the application is greater
than when you have the code in one place.

Clearly, the benefits of using procedures are numerous and important.
Procedures are the foundation of a good program, and you will
see them used throughout the rest of the applications in this
book.
Event
Procedures

Earlier this week you were exposed to procedures, although you
may not have known it at the time. When you work with controls
and other components, you frequently interface with them using
event procedures. Event procedures are subroutines that
are called automatically by the browser. They are different
from regular subroutines in that regular subroutines must be called
within the program by statements you write or else they are never
used. An event procedure is called as a result of some action
taken by the user or the system, such as the user clicking a command
button or checking a box on a Web page or the system detecting
that a predefined timer has expired.

An event is a subroutine called automatically by VBScript
as a result of a user or system action. Subroutine and function
calls must be manually specified by the programmer through normal
calls in code, but event subroutines are called automatically
as a result of what they represent. You just define a subroutine
to be carried out when the user clicks on a particular button
by means of a special name, for example. Then the button control
object itself generates an OnClick
event when it is clicked on, and VBScript responds to this event
by calling the named subroutine associated with the event.

You don't have to worry about calling event procedures in your
code because they are called automatically by the system and the
browser, but you do need to create them if you want to write code
that responds to these events. For instance, if you place a button
on your Web page, you probably want your program to respond in
some way when the user clicks it. If you construct an event procedure,
you can write the code to do just that. If you fail to place the
event subroutine in your code, however, your users can click the
button all day long, and nothing will happen because there is
no event procedure to process the click events.

The rules for creating and naming event procedures are more rigid
and structured than regular procedure naming rules. First of all,
the naming conventions for an event procedure are very specific.
Except with some special cases discussed on Day 8,
"Intrinsic HTML Form Controls," you can't just name
an event procedure anything you want. To name an event procedure,
you must know two things: the name of the control, or component,
and the name of the event you want to respond to. Suppose, for
example, that you have a command button on your Web page labeled
TestButton. As you
will see on Day 8, buttons have an event
called OnClick that you can
write code for. This event occurs when the user clicks
the button. To create an event procedure for this action, you
must name your procedure

TestButton_OnClick


Notice that the name of the control comes first, followed by an
underscore character, followed by the name of the event. You must
spell everything correctly, or else VBScript will be unable to
connect the event procedure to the button. The rule for assigning
a name to a control or component is

Sub ControlName_EventName()


where ControlName
is the name of the control and EventName
is the name of the event corresponding to the control. In
addition to what you'll learn on Day 8,
you will also learn about intrinsic HTML controls and ActiveX
controls on Day 9, "More Intrinsic
HTML Form Controls," Day 10, "An
Introduction to Objects and ActiveX Controls," and Day 11,
"More ActiveX Controls." On those days, you will learn
much more about event procedures and how to use them. For now,
just keep in mind that event procedures are blocks of code grouped
in subroutines within your scripts, just like those we have already
discussed.
Method
Procedures

You may have also heard of method procedures. Method procedures
are like predefined procedures you can call, but they are provided
by an object. You can't actually see the code for them, nor can
you create them. Objects can have a set of methods that were created
when the programmer designed the object.

A method is a procedure you can call that is associated
with an object. Methods accomplish some specific task or service
the object provides. Methods may or may not return values, depending
on what they do.

For example, the error object, which you will learn more about
on Day 17, has a method called clear
that will clear the error number of the error that has occurred.
To invoke that method, you can simply call the method using the
following convention:

object.method


where object is
the name of the object and method
is the name of the method. In this case, you could use

Err.Clear


to clear the error code for the error that has occurred. Methods
are different from events, functions, or subroutines, however,
because you cannot create them. They are an inherent part of an
object that you can call.
Procedures,
Subroutines, Functions, Events, and Methods!

Wow! What a collection of terms. It's easy to get confused in
recognizing these terms, so think of them as shown in Figure 7.4.

Figure 7.4 : Sorting out procedures, subroutines, functions, events, and methods.

Procedures consist of subroutines and functions. Functions can
return a value to the caller, and subroutines cannot. Events are
special subroutines that are called automatically in response
to the user triggering an event, such as clicking a button. Finally,
methods are a special kind of procedure that can either be a function
or a subroutine. That is, they could return a value or they might
not. The only difference is that methods are part of an object.
You can call a method in your code if you have access to an object,
but you cannot create them or see the code that is executed behind
them.
Where
to Put Procedures

Finally, you need to understand where you can place procedures
in your HTML document. On Day 3, "Extending
the Power of Web Pages with VBScript," you learned about
the general structure of an HTML document. You saw that, as a
rule, you should put VBScript code before the end of the <BODY>
section of an HTML document. You can place procedures within one
script tag:

<SCRIPT LANGUAGE="VBScript">

<!--
     Sub GetMiles()
          ...code
for subroutine
     End Sub

     Function CalculateTime(RunnerTime)
          ...code
for function
     End Function
-->
</SCRIPT>


You can also place procedures in separate scripts:

<SCRIPT LANGUAGE="VBScript">

<!--
     Sub GetMiles()
          ...code
for subroutine
     End Sub
-->
</SCRIPT>

<SCRIPT LANGUAGE="VBScript">
<!--
     Function CalculateTime(RunnerTime)
          ...code
for function
     End Function
-->
</SCRIPT>


A good rule of thumb is to enter all the subroutines and functions
first, making sure none is called before it is used. Also, it
is much easier to put them as close together as possible, preferably
all under the same script tag. That way, you have access to all
of them at once and don't have to hunt through your Web page to
find them.

Also, notice that not all VBScript code must be stored within
a procedure. If you have code outside a procedure, such as the
example shown below, keep in mind that this code is automatically
executed in order from top to bottom when the browser first loads
the Web page:

<SCRIPT LANGUAGE="VBScript">

<!--  Option Explicit

   Dim Miles_Ran
   Dim Total_Miles
   Dim Start_Time
   Call InitializeVariables

   Sub InitializeVariables
      Miles_Ran = 0
      Total_Miles = 0
      Start_Time = 0
   End Sub
-->


When the browser first loads the Web page, it's a good time to
initialize variables, call procedures that set values to their
default conditions, and so on. As a rule, it's usually better
to place all such code in the same place so that you don't miss
a line or two somewhere down the page. Likewise, it's a good idea
to place your script, and all its startup code, at the bottom
of your page prior to the </BODY>
tag. This ensures that any objects your startup code references
have already been defined in the page by the time the code is
called as the page loads for the first time. If your code doesn't
use any startup code executed as the page loads, this is not a
necessary step, but still is a recommended convention for easy
script viewing and maintainability.
Summary


Today you have learned how to create and use procedures in your
VBScript programs. You can divide procedures into two types: subroutines
and functions. Subroutines and functions both contain code that
other parts of the application can call. You have seen that a
function returns a value to the user when it's finished, whereas
a subroutine does not. You have also discovered how to declare
a function or subroutine and call it in other places throughout
the application.

Both functions and subroutines can accept one or more arguments
that they can use to perform their tasks. You can pass either
a copy of the arguments or a reference to the actual values to
a procedure. When passing variables by value, those variables
can be changed because they are copies of the original. In the
case of passing variables by reference, however, you can read
but not change the variables, because the procedure does not "own"
them. You have learned how to pass variables into procedures using
both ways and saw practical examples of both cases.

In studying how to construct procedures, you have discovered why
they are so useful in the first place. You have learned that procedures
help make your code more maintainable, more readable, and more
prone to be free of errors. These are significant reasons to use
procedures in your application wherever it is practical.

You have also been introduced to a special type of procedure called
an event procedure. Event procedures are different from
ordinary procedures in that they are called by the system and
browser as a result of some activity initiated by the user or
some other component on the Web page, such as a timer. Today's
lesson briefly introduces event procedures, and next week you'll
learn how and why you use them.

Today's lesson also discusses methods, which are special procedures
contained within an object. You can call these procedures in your
code by referencing them through the object. Although you can't
modify a method or see the code, you can call it to accomplish
a specific task within the object.

Finally, you have learned how and where to place procedures in
your Web page. You can put the procedures all together within
one script tag or spread them across multiple script tags. To
make your code more readable, you should place all your subroutines
in the same script tag in one section. That way, you can debug
and find procedures without hunting for them all over your page.

The purpose of today's lesson is to give you a practical grasp
on how to create and use procedures. Throughout the rest of the
book, we use procedures. Indeed, they have been used for quite
some time, although you might not have recognized them. Today's
lesson is the last of a series of lessons that presented you with
the basics of VBScript programming. Next week you'll put the basics
to work and have some fun writing some Web pages of your own.
Q&A





QHow many arguments can you give a procedure?

AAt the time of this printing, VBScript allowed you to pass up to 127 arguments to a procedure if you wanted. In reality, however, a good suggestion is that you not enter more than 10. The
reason for this is that the more parameters you enter, the less readable and maintainable your procedures will be. If you find yourself passing more than ten parameters, you might want to redesign the procedure, possibly breaking it up into smaller pieces.


QWhat happens if I forget to include an event procedure for a control on my Web page?

ADon't worry; nothing dangerous will happen on your Web page. The particular event that occurs in the control or component on the page simply won't find an event in your code to connect to and
therefore will not perform any of your code statements. Of course, you should make sure event procedures exist for every control on your form where you want your code to respond. You will learn much more about this in next week's lessons.


Workshop


Create a Web page that uses VBScript code to ask the user for
10 values. All 10 values must be between 0 and 10. Get all 10
values from the user and make sure they are valid, but do not
use any procedures in your code. Now, change the validation rule
to accept values between 0 and 75 and test your Web page again.
After you've done this, apply the same approach again, but this
time, use procedures. By going through this exercise, you will
begin to appreciate the value of using procedures in your code.
Quiz





Note


Refer to Appendix C, "Answers to Quiz Questions," for the answers to these questions.





What is the difference between a function and a subroutine?
Given the following code listing, what is the value of the
variable A that is passed
into the function after the function has been called successfully?



Sub cmdTest_OnClick()
   Dim A, B, C
   A = txtA.Value
   B = txtB.Value
   C = GetHypotenuse(A, B)
   MsgBox "The hypotenuse of the right triangle
is " & C & " units."
End Sub

Function GetHypotenuse(ByVal A, ByVal B)
   A = A * A
   B = B * B
   If A + B >= 0 Then
      GetHypotenuse = Sqrt(A + B)

   Else
      GetHypotenuse = 0
   End If
End Function

The following code listing contains an error:

Sub cmdTest_OnClick()
   
    Dim Base_Cost
    Dim Total_Cost
    Dim Tax

    Tax = 5     ' Michigan
sales tax (5%)

    Total_Cost = CalculateCost(Base_Cost,
Tax)

    txtResult.Value = "The total cost
is $" & Total_Cost

End Sub
Function CalculateCost(Cost, Tax)

   Tax = Tax / 100

   Cost = Cost + Tax * Cost

   CalculateCost = Cost

End Function

Find the error and fix the code listing so that it works properly.













Wyszukiwarka

Podobne podstrony:
T 14
Rzym 5 w 12,14 CZY WIERZYSZ EWOLUCJI
ustawa o umowach miedzynarodowych 14 00
990425 14
Cisco2 ch7 Vocab
foto (14)
DGP 14 rachunkowosc i audyt
Plakat WEGLINIEC Odjazdy wazny od 14 04 27 do 14 06 14
022 14 (2)
index 14
Program wykładu Fizyka II 14 15

więcej podobnych podstron