Variables and Values in VBScriptBy Bill Wilkinson
One of the most used but least understood features of VBScript is the flexibility
of variables and values as used throughout the language. This short series of
articles will hopefully remove some of the mysteries of VBScript for you.
In this first article, we will explore the basic principles of variables and values. In succeeding articles, we will discuss arrays, user-defined objects, and more.
The second part, Variables, Values, and Arrays in VBScript, examines how VBScript handles single and multi-dimensional arrays.
Finally, the third part, Object References in VBScript, looks at object references and how VBScript handles copies of objects and arrays.
Introduction to Variants:
If you are a C or C++ programmer experienced in COM and OLE, you will hopefully know what I mean when I say that every variable and every value (except certain intermediate values) in VBScript is actually an instance of an OLE VARIANT type. If you don't know what that means, read on.
To begin, let's talk about what variables look like in most other languages. In C
and C++ and Java and Pascal and even Visual Basic, you typically tell the language
processor (compiler or interpreter) what kind or type of variable you are creating.
For example, you would do the following to create a variable named
that is capable of holding values from roughly
+2,000,000,000 in these various languages (although the actual range may
depend on the particular compiler you are using):
- Visual Basic:
Dim myNumber AS Long
myNumber : Integer;
But in VBScript, all variables are always of the same kind or type, the aforementioned VARIANT type. And just what is a VARIANT? Ahhh… A VARIANT is a special kind of data structure that is capable of holding or referring to any kind of data that OLE (or VBScript) understands! And what it holds can vary, from moment to moment! Actually, you may have seen this in VBScript code, such as:
If you didn't already know it, be aware that
Request.QueryString("fieldname") always return a string!
This only makes sense: The browser certainly doesn't know the intended use of any
given form field, so it always sends all data on to the server as a string. And
just because a string contains
17 November 2001 doesn't mean, to the
server, that it is necessarily a date. So, if you want to ensure that a given form
field contained a date - or if you need to do datewise manipulation of a value from
such a field - you use the
CDate function. So that same variable first
held a string and then held a date/time value! This would be impossible with the
built-in data types found in most other languages.
How does it work?
Inside your computer's CPU (presumably, a Pentium processor, unless you are lucky enough to be running ChiliSoft ASP on Solaris or AIX?), there are certain data types that it understands in ways that are built into its hardware. Because these are so fundamental to computer processing, they are sometimes called "primitive" types. When a primitive type appears in your computer's memory (in RAM or ROM or even on a disk), it occupies a certain number of bytes, which number is fixed for each primitive type.
Examples: A C/C++/Java
as shown above) always occupies four (4) bytes of memory. A short format floating point
number (which can represent numbers with a decimal point with roughly 6 or 7 digit
accuracy) also uses four bytes. A long format floating point number (also known as
a "double", having roughly 14 or 15 digits of precision) needs 8 bytes. Various
other primitive types occupy from one to eight bytes of memory. (Theoretically, a
boolean needs only one bit of memory, but in practice most computer languages use one
or more bytes because that fits the computer CPU architecture better.)
In the "conventional" languages, then, when you create a variable of a particular type
(as in the various integer examples, above), that variable is always exactly that
size. You can't store a
double in a variable that was allocated as an
integer (or vice versa—even though the 4-byte integer would fit in the
8 byte double, the languages don't allow it for reasons too complex to go into here).
But then there is VARIANT. If you happen to know Pascal, you might recognize a VARIANT as simply a "varying record." C and C++ don't have any built-in type that corresponds (and you really can't do this in Java, at all), but sometimes you might hear the term "discriminated union" used to describe this kind of structure. Briefly, a VARIANT always occupies 16 bytes of memory but only uses what is needed. And the first two bytes of the VARIANT always indicate what the other 14 bytes are holding!
So if you do something in VB like this:
The variable num will have its first two bytes set to the number 2 (indicating a short integer) and two other bytes of the 16 (a particular two—and which two of the 16 never changes, but we don't care which two for this discussion) will hold the integer number 731. If you do:
then the first two bytes of num will contain the number 3 and a particular four bytes
of the 16 will contain the long integer value 12345678. If you are curious about
what all the possible values of those first two bytes can be with VBScript, then
look at the documentation in the VBScript reference manual for the
VarType function, where all the values are listed (view
the technical docs).
(Not relevant to
this discussion, but of interest to C/C++ programmers, perhaps: There are many more
possible values for those two bytes within the definition of an OLE VARIANT data
type. But VBScript uses only the values noted under the documentation for
So now we can understand how the code we looked at above works:
The first assignment to the variable
uDate will set its first two bytes
to 8, indicating that the VARIANT contains a string. The second assignment changes that
number to a 7, indicating that the variable now contains a Date/Time value. And, in
the process, the other bytes of the VARIANT are changed as needed.
"But wait," I can hear you say, "if I have a string of 100 characters how do they
all fit into those 16 bytes of the VARIANT?" Ahhh…you knew there had to be trick in
here! And the trick is simple: For all except the primitive data types (look above
to see what they are), the VARIANT contains a pointer to the actual data! So if
you look at the list of type numbers in the documentation
VarType, you will hopefully understand that the following numbers do
not represent primitive types and so the rest of the VARIANT will contain a pointer
to the actual data:
|A pointer to the string contents|
|A pointer to an ActiveX object|
|A pointer to an Error object|
|Please don't ask about this one yet!|
|A DAO object (may not be used in ASP?)|
Looking again at the
uDate function example, above, we can see that a
given variable can sometimes hold a VARIANT that contains a primitive value and
sometimes one that contains a pointer to some more extended data. Truly, VBScript variables,
through the mechanism of the VARIANT, are extremely flexible.
And now, before we leave this subject for this week, let's look at an interesting little ASP page:
I won't make you actually try that code, though I do encourage you to do so. And even experiment with it some. But to save you time, here is what that page looks like in a browser:
from d1(second) we get: this via d2.add from d2(first) we get: added via d1.add
Do you understand the implications of that? The "objects" referenced by the
d2 are actually one and the same object!
did not copy the object referenced by
d1. Instead, only the pointer
d1 contains is copied to a pointer in
d2. The actual
object does not move, is not affected by the copy, and remains fully accessible to
Start considering the implications of that seemingly simple action. And then next time we can discuss how this relates to other features of VBScript and start discussing how arrays work.