|
CSCI A348/548
|
A. Lexical Structure
// and
the end of a line is treated as a comment. Also, any text between the
characters /* and */ is treated as a comment.
[-](1-9)(1-9)*
[-]0(0-7)*
[-]0(x|X)(0-9|a-f|A-F)*
[(+|-)][digits][.digits][(E|e)[(+|-)]digits]
\) can be used in escape sequences.
(true|false)
null literal: this keyword represents a lack of value.
if, while,
break and so on. For a complete list check the reference pages. B. Variables and Data Types
var keyword:
var i; var sum;
For this reason variable declaration in JavaScript (withi = 10; i = "ten";
var) does not specify a
type. Also, because of JavaScript's lack of typing, values are conveniently and automatically
converted from one type to another.
+, -, *, and /.
Math and so we always need to use the literal Math
to access them. For example to compute the square root of a numeric expression we might use code
like this:
hypot = Math.sqrt(x*x + y*y);
Infinity. There's also an -Infinity value. When a mathematical
operation (such as division by zero) yields an undefined result or an error the result is
the special "not a number" value printed as NaN. The special function
isNaN() is required to test for this value as NaN does not compare
equal to any number including itself.
+ operator, as in Perl. To
determine the length of a string you use the length property of the string. JavaScript
does not have a character data type. To represent a single character you simply use a string that
has a length of 1. There are a number of methods that you can use to operate on
strings: charAt(), substring(), indexOf(), etc. Here are
some examples:
msg = "Hello, " + "world."; // concatenates strings
s = msg.substring(7, 9); // extracts chars 6, 7, and 8
letter = s.charAt(s.length - 1); // letter is set to 'r'
i = msg.indexOf('e'); // i is set to 1
Here are two more: s = "These are the times of miracle and wonder.";
last word = s.substring(s.lastIndexOf(" ") + 1, s.length);
true or false, usually as
the result of comparisons in our programs. Used in if, while,
and various other conditional control structures.
function square(x) {
return x * x;
}
Once a function is defined you can invoke it by following the function's name with a
comma separated list of arguments within parentheses. The following lines are function
invocations:
An unusual feature of JavaScript is that functions are actual data types. This means that functions can be stored in variables, arrays, and objects, and it means that functions can be passed as arguments to other functions. Since functions are data types they can be assigned to object properties just like other values can. When a function is assigned to a property of an object it is often referred to as a method of that object.y = square(x); click(); y = sin(x); compute_distance(x1, y1, z1, x2, y2, z2);
image has properties named width and height we
can refer to those properties like this: Properties of objects are, in many ways, just like JavaScript variables and can contain any type of data, including arrays, functions, and other objects. Thus you might see JavaScript code like this:image.width image.height
which refers to thedocument.myform.button
button property of an object which is itself stored in the myform
property of an object named document.
To invoke a method of an object we use the . syntax to extract the function
value from the object, then use the () syntax to invoke that function.
Objects in JavaScript have the ability to serve as associative arrays - that is, they can associate arbitrary data values with arbitrary strings. When objects are used in this way, a different syntax is generally required to access the object's properties: a string containing the name of the desired property is enclosed with square brackets. Here's the width and the height of the image object accessed again this time as associations:
image["width"] image["height"]
refers to thedocument.images[1].width
width property of an object stored in the second element
of an array stored in the images property of the document
object. Because JavaScript is an untyped language the elements of an array do not all need to be of the same type as they do in typed languages like Java.
null
you know it does not contain a valid object or array, number, string, Boolean or
function.
null value, there is no
undefined keyword for the undefined value. This can make it hard to write
JavaScript code that detects this undefined value. The undefined value is not the same
as null, but for most practical purposes, you can treat it as if it is. This
is because the undefined value compares equal to null.
The DateObject. The sections above described all of the
fundamental data types supported by JavaScript. Dates and times are not one of these fundamental
types. But JavaScript does provide a type (or class) of object that represents
dates and times, and can be used to manipulate this type of data. A Date object
in JavaScript is created with the new operator and the Date() constructor.
Here are some examples:
now = new Date(); // [1]
xmas = new Date(96, 11, 25); // [2]
xmas.setYear (xmas.getYear() + 1); // [3]
document.write("Today is: " + now.toLocaleString()); // [4]
Here's what the statements do:
Date object representing Christmas '96
String and Function
objects, and less useful for Numbers and Strings.
The following table summarizes the JavaScript operators:
| Prec. | Assoc. | Operator | Operand Type(s) | Operation Performed | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | L | . | object, property | property access | |||||||||||
| L | [] | array, integer | array index | ||||||||||||
| L | () | function, args | function call | ||||||||||||
| 1 | R | ++ | number | pre-or-post increment (unary) | |||||||||||
| R | -- | number | pre-or-post decrement (unary) | ||||||||||||
| R | - | number | unary minus (negation) | ||||||||||||
| R | ~ | integer | bitwise complement (unary) | ||||||||||||
| R | ! | Boolean | logical complement (unary) | ||||||||||||
| R | typeof | any | return data type (unary) | ||||||||||||
| R | new | constructor call | create new object (unary) | ||||||||||||
| R | void | any | return undefined value (unary) | ||||||||||||
| 2 | L | *, /, %
| numbers | multiplication, division, remainder | |||||||||||
| 3 | L | +, -
| numbers | addition, subtraction | |||||||||||
| L | + | strings | string concatenation | ||||||||||||
| 4 | L | << | integers | left shift | |||||||||||
| L | >> | integers | right shift with sign-extension | ||||||||||||
| L | >>> | integers | left shift with zero-extension | ||||||||||||
| 5 | L | <, <=
| numbers or strings | less than, less than or equal | |||||||||||
| L | >, >=
| numbers or strings | greater than, greater or equal | ||||||||||||
| 6 | L | == | primitive types | equal (have identical values) | |||||||||||
| L | != | primitive types | equal (refer to the same object) | ||||||||||||
| L | == | reference types | equal (refer to the same object) | ||||||||||||
| L | != | reference types | not equal (refer to different objects) | ||||||||||||
| 7 | L | & | integers | bitwise AND | |||||||||||
| 8 | L | ^ | integers | bitwise XOR | |||||||||||
| 9 | L | | | integers | bitwise OR | |||||||||||
| 10 | L | && | Booleans | logical AND | |||||||||||
| 11 | L | || | Booleans | logical OR | |||||||||||
| 12 | R | ?: | Boolean, any, any | conditional (ternary) operator | |||||||||||
| 13 | R | = | variable, any | assignment | |||||||||||
| R |
| variable, any | assignment with operation | ||||||||||||
| 14 | L | , | any | multiple evaluation |
?:
which combines the value of three expressions into a single expression.
*) strings.
Note however that JavaScript tries to convert expressions to the appropriate type whenever
possible so an expression like is legal although"3" * "5"
isn't. Furthermore some operators behave differently depending on the type of the operands. Most notably the"a" * "b"
+ operator
adds numeric operands but concatenates string operands.
the multiplication operator has a higher precedence than the addition operator so multiplication will be performed first, followed by the addition. Furthermore the assignment operator (w = x + y * z;
=) has the lowest precedence and so it is
performed at the end, after all the operations on the right-hand side are
completed. Operator precedence can be overridden with the explicit use of
parentheses.
L specifies left
to right associativity and a value of R specifies right to left associativity.
The associativity of an operator specifies the order in which operations of the same
precedence are performed. Notice that operators of the same precedence (in the table)
have the same kind of associativity. Example:
is essentially equivalent to:w = x + y + z;
sincew = ((x + y) + z);
+ is a binary operator with left to roght associativity. We now look again at the operators, from a different point of view.
+), subtraction (-), multiplication
(*), division (/), modulo (%), unary negation
(-), increment (++), decrement (--) you are
already familiar with them.
true or false) depending on the result
of the comparison. They are most commonly used in things like if statements and
while loops. We can distinguish: ==) is an operator that returns
true if its two operands are equal, and returns
false if they are not equal. The operands may be of any type,
and the definition of "equal" depends on the type: in JavaScript, numbers,
strings and Boolean values are compared by value, objects
and arrays are compared by reference. Note that the equality operator
(==) is very different from the assignment operator (=).
!=) is an operator that
tests for the opposite of ==
<), greater than (>), less than or equal
(<=), greater than or equal (>=) are operators for numbers.
+ concatenates two string
operands. <, <=, >
and >= compare two strings to determine what order
they fall in. The comparison uses alphabetical order.
&&),
or (||), not (!), expect their operands to be
Boolean values and they perform "Boolean algebra" on them.
= is used in JavaScript to assign a value to a variable,
for example:
While you might not normally think of such a line of JavaScript as an expression that has a value and can be evaluated, it is in fact an expression, and technically speaking,i = 0;
= is an operator. The =
operator expects its left-hand operand to be a variable, or the element of
an array or a property of an object, and expects its right-hand operand to
be an arbitrary value of any type. The value of an assignment expression is
the value of the right-hand operand. As a side-effect, the =
operator assigns the value on the right to the variable, element, or property
on the left so that future uses of the variable, element, or property refer to
the value. Because it is defined as an operator you can include it in more
complex expressions. For example you can assign and test a value in the same
expression with code like this:
If you do this, be sure you are clear on the difference between(a = b) == 0
=
and ==. Also, because of it being an operator with left to right associativity you can have multiple assignnment operators such as:
to assign a single value to multiple variables.i = j = k = 0;
JavaScript also supports a number of assignment operators that provide a shortcut by combining assignment with some other operation. In general
wherea = op= b;
op is an operator is equivalent to:
wherea = a op b;
op is one of the
following:
+,
-,
*,
/,
%,
<<,
>>,
>>>,
&,
|,
^
The conditional operator (?:) is the only
ternary operator (three operands) in JavaScript and is actually
called the ternary operator. Because the operator has three operands,
the first goes before the ?, the second one goes between
? and : and the third one goes after the
:. It is used like this:
The first operand must have a Boolean value - usually this is the result of a comparison expression. The second and third operands may have any value. The value returned by the ternary operator depends on the Boolean value of the first operand. If that operand isx > 0 ? x * y : - x * y;
true,
then the value of the conditional expression is the value of the second
operand. Otherwise the value returned is the value of the third operand.
This operator is used as a shortcut for if statements.
The typeof operator is an unary operator that is placed
before its single operand, which can be of any type. The value returned is a
string that indicates the data type of the operand.
The object creation operator (new) has the following syntax
where constructor must be a function-call expression. It is a unary operator. It works as follows: it creates a new object with no properties defined. Next, it invokes the specified constructor function, passing the specified arguments, and passing the newly created object as the value of thenew constructor
this
keyword. The constructor function can then use the this keyword
to initialize the new object in any way desired.
The delete operator. This operator simply sets its operand
(a variable, object property, or array element) to null. This is an
operator that is fairly deprecated, but check with the documentation for your
version of JavaScript.
The void operator discards the operand value and
returnes an undefined value. This is a unary operator.
The comma operator is a simple one: it evaluates its left argument, then its right arguments, then returns the value of the right argument. Thus, this line:
is equivalent to the three assignments one after another. This is of limited use, most often ini = 0, j = 1, k = 2;
for loops and those instances
when you want to evaluate several independent expressions with side
effects in situations where only one expression is syntactically allowed.
Array and Object Access Operators. You can access elements of an array using
square brackets [] and you can access elements of an object using a dot
(.); both of these are treated as operators in JavaScript. The dot
operator expects an object as its left operand and the name of an object property or
method as the right operand. This right operand should not be a string or a variable
that contains a string, but should be the literal name of the property, without quotes
of any kind. Here are some examples:
document.lastModified
navigator.appName
frames[0].length
document.write("hello world")
If the specified property does not exist in the object, JavaScript
does not issue an error, but instead simply returns the special undefined
value as the value of the expression.
The [] operator allows access to array elements
and also to object properties, and it does so without the
restrictions that the dot operator places on the right-hand
operand. If the first operand (which goies before the left
bracket) refers to an array, then the second operand (which goes between the
brackets) can be an arbitrary expression that evaluates to an integer. For example:
If the first operand to the bracket operator is a reference to an object, on the other hand, then the second operand may be an arbitrary expression that evaluates to a string that names a property of the object. Note that in this case, the second operand is a string, not a literal name. It should be a constant in quotes, or a variable or expression that refers to a string. This works like associative arrays in Perl and awk programming languages. For example:frames[1] document.forms[i + j] document.forms[i].elements[j++]
Thedocument["lastModified"] frames[0]['length'] data["val" + i]
[] operator is usually used to access the elements
of an array. It is less convenient than the . operator
for accessing properties of an object because of the need to quote the name
of the property. When an object is used as an associative array, however,
and the property names are dynamically generated, then the .
operator cannot be sued, and only the [] will do. This is commonly
the case when you use the for/in loop reviewed in the next section.
For example, here's a way to print out the name and value of all properties
f in an object o:
for (f in o) {
document.write('o.' + f + ' = ' + o[f]);
document.write('<br>');
}
The function call operator () is used to invoke
functions. This is an unusual operator in that it does not have a fixed
number of operands. The first operand is always the name of a function,
or an expression that refers to a function. This is followed by the left
parenthesis and any number of additional operands, which may be arbitrary
expressions, each separated from the next with a comma. The right parenthesis
follows the final operand. the () operator evaluates each of
its operands, and invokes the function specified by the first, with the
value of the remaining operands passed as arguments.
D. Statements
A JavaScript program is simply a collection of statements.
The simplest kind of statements in JavaScript are expressions that have side effects. One major category of these are assignment statements. For example:
Relating to assignment statements are the increment and decrement operatorss = "Hello " + name; i *= 3;
++ and --. These have the side effect
of changing a valriable value, just as if an assignment had been performed:
Function calls are another major category of expression statements.counter++;
JavaScript has a way to combine a number of statements into a single statement, or statement block. This is done simply by enclosing any number of statements within curly braces. Thus the following lines act as a single statement and can be used anywhere that JavaScript expects a single statement.
{
x = Math.PI;
cx = Math.cos(x);
alert("cos(" + x + ") = " + cx);
}
Note that although this statement block acts as
a single statement, it does not end with
a semicolon. The primitive statements within the
block end in semicolons, but the block itself does
not.
if statement.
This is the fundamental "control statement"
that allows JavaScript to "make decisions". It
has two forms:
andif (expression) statement
When you have nestedif (expression) statement1 else statement2
if statements
with else clauses, some caution is
required to ensure that the else
clause goes with the appropriate if
statement. Consider the following lines:
i = j = 1;
k = 2;
if (i == j)
if (j == k)
document.write("i equals k");
else
document.write("i doesn't equal j"); // WRONG!!
In this example the inner if statement forms the single statement
allowed by the syntax of the outer if statement. The rule in
JavaScript (as in most programming languages) is that an else clause
is part of the nearest if statement. It's safer to use curly braces
to precisely communicate the intended meaning:
i = j = 1;
k = 2;
if (i == j) {
if (j == k)
document.write("i equals k");
} else
document.write("i doesn't equal j");
while loop.
Here's an example while loop:
count = 0;
while (count < 10) {
document.write(count + "<br>");
count++;
}
for loop. The syntax is
and itsfor(initialize ; test ; increment) statement
while equivalent is:
initialize ;
while (test) {
statement
increment;
}
for ... in statement. This
statement is a somewhat different kind of loop with the following syntax:
for (variable in object) statement
The variable should be the name of a variable, or
sjould be the element of an array or a property of an object; it should be
something suitable as the left-hand side of an assignment expression.
You
can loop through the elements of an array by simply incrementing
an index variable each time through a while or
for loop. The for/in statement provides a way to
loop through the properties of an object. The body of the
for/in loop is executed once for each property of
object. Before the body of the loop is
executed, the name of one of the object's properties is assigned
to variable, as a string. Within the body of
the loop, you can use this variable to look up the value of the
object's property with the [] operator.
For example, the
following for/in loop prints out the name and value of each
property of an object:
for (prop in my_object) {
document.write("name" + prop +
"; value: " + my_object[prop] + "<br>");
}
The for/in loop does not specify in what order the properties of
an object will be assigned to the variable. There is no way to tell in advance
and the behavior may differ between implementations or versions of JavaScript.
break statement. This statement is
valid only within the body of a while,
for, or for/in loop. When executed
it exits the currently running loop. The following example searches
the elements of an array for a particular value. If the value is found, a
break statement terminates the loop:
for (i = 0; i < a.length; i++) {
if (a[i] == target)
break;
}
continue statement. It
is related to the break statement and has a syntax that
is just as simple. Like the break statement,
continue can be used only within the body of a
while, for, or for/in
loop. When executed the current iteration of the enclosing loop
is terminated and the next iteration begins. The following
example shows the continue statement being used
to abort the current iteration of a loop when an error occurs:
for (i - 0; i < data.length; i++) {
if (data[i] == null)
continue; // can't proceed with undefined data
total += data[i];
}
with statement. JavaScript interfaces with the web browser through an "object hierarchy" that contains quite a few arras nested within objects and objects nested within arrays. In order to refer to the components that make up a web page, you may find yourself referring to objects with cumbersome expressions like the following:
Theframes[1].document.forms[0].address_field.value
with provides a way to simplify expressions like this one, and
reduce your typing. It has the following syntax:
For example you migh use thewith (object) statement
with to
simplify the following code:
Usingx = Math.sin(i * Math.PI / 20); y = Math.cos(i * Math.PI / 30);
with, you might write:
with(Math) {
x = sin(i * PI / 20);
y = cos(i * PI / 30);
}
var statement
provides a way to explicitly declare a variable or variables.
Examples:
Thevar i; var j = 0; var x = 2.34, y = 4.12, r, theta;
var statement should always be used when
declaring local variables within functions. Otherwise you run
the risk of overwriting a top-level variable of the same name.
Note that the var statement can also legally appear
as part of the for and for/in loops,
in order to declare the loop variable as part of the loop itself.
For example:
for (var i in o) document.write(i, "<br>"); for (var i = 0; i < 10; i++) sum += i;
function statement. This statement is used to define a new function. Here are some examples:
function welcome () {
alert("Welcome to my home page!");
}
function print(msg) {
document.write(msg, "<br>");
}
function hypotenuse (x, y) {
return Math.sqrt(x * x + y * y);
// return is documented in next section
}
function factorial (n) { // a recursive function
if (n <= 1) return 1;
else return n * factorial(n -1 );
}
The most important way that the function statement
differs from other statements is that the statements that form the
body of the function are not executed. Instead, they are
stored as the definition of a new function and may be executed at
a later time with the () function call operator.
return statement is
used to specify the value "returned by" a function. For
example:
function double(x) {
return 2 * x;
}
The return statement may also be used without
an expression to simply terminate execution of the function
without returning a value.
One final legal statement in JavaScript is the empty statement. It looks like this:
Executing the empty statement obviously has no effect and performs no action. You might think that there would be little reason to ever use such a statement, but it turns out that the empty statement is occasionally useful when you want to create a loop that has an empty body. For example:;
To make your code clear, it can be useful to comment your empty statements as such:// initialize an array a for (i = 0; i < a.length; a[i++] = i) ;
for (i = 0; i < a.length; a[i++] = i) /* empty */ ;
E. Functions
Functions are an important and complex part of the JavaScript language.
Functions are defined with the function
keyword, followed by:
Here are some examples of functions:
1. A shortcut function, sometimes useful instead of
document.write(); This function has no return
statement, so it returns no value.
function print(msg) {
document.write(msg, "<br>");
}
2. A function that computes and returns the distance between two points.
function distance(x1, y1, x2, y2) {
var dx = (x2 - x1);
var dy = (y2 - y1);
return Math.sqrt(dx * dx + dy * dy);
}
3. A recursive function (one that calls itself) that computes factorials.
Recall that x! is the product of x and all positive
integers lass than it.
function factorial(x) {
if (x <= 1) return 1;
else return x * factorial(x - 1);
}
Once a function has been defined it may be invoked with the
() operator:
print("Hello, " + name);
print("Welcome to my home page!");
total_dist = distance(0,0,2,1) + distance(2,1,3,5);
print("The probability of that is: " +
factorial(13)/factorial(52));
The most important features of functions is that they can be defined and invoked, as shown in the previous section. In JavaScript functions are not only syntax but also data.
To understand how functions are JavaScript data as well as JavaScript
syntax, we've got to understand what the function really
does. function creates a function, as we've seen, but it
also defines a variable. In this way, the function
keyword is like the var keyword. Consider the following
function definition:
function square(x) { return x * x; }
This code does the following:
square.
x, and has a body that consists of a
single statement: return x * x;
function square(x) { return x * x; }
a = square(4); // a contains the number 16
b = square; // now b refers to same function as square
c = b(5); // c contains the number 25
Functions can also be assigned to object properties:
Functions don't even require names, as when we assign them to array elements:o = new Object; o.sq = square; y = o.sq(16); // y equals 256
Note that the function invocation syntax in this last example looks strange, but is still a legal use of the JavaScript function call operator.a = new Array(10); a[0] = square; a[1] = 20; a[2] = a[0](a[1]); // a[2] contains 400
The next example is a detailed illustration of the things that can be done when functions are used as data. The example may be a little tricky, but the comments explain what is going on; it is worth studying carefully.
// We define some simple functions here
function add(x, y) { return x + y; }
function subtract(x, y) { return x - y; }
function multiply(x, y) { return x * y; }
function divide(x, y) { return x / y; }
// Here's a function that takes on of the above functions
// as an argument and invokes it on two operands
function operate (operator, operand1, operand2) {
return operator(operand1, operand2);
}
// We could invoke this function like this to compute
// the value (2 + 3) + (4 *5)
var i = operate(add, operate(add, 2, 3),
operate(multiply, 4, 5));
// Now we store the functions defined above
// in an associative array
var operators = new Object();
operators["add"] = add;
operators["subtract"] = subtract;
operators["multiply"] = multiply;
operators["divide"] = divide;
operators["pow"] = Math.pow;
// works for predefined functions too
// This function takes the name of an ooperator, looks up
// that operator in the array, and then invokes it on the
// supplied operands. Note the syntax used to invoke the
// operator function.
function operate2(op_name, operand1, operand2) {
if(operators[op_name] == null) return "unknown operator";
else return operators[op_name](operand1, operand2);
}
// We could invoke this function as follows to compute
// the value ("hello" + " " + "world"):
var j = operate2("add", "hello",
operate2("add", " ", "world"));
Each JavaScript primitive has a corresponding "wrapper". Since functions
are not just a syntactic feature, but also a data type, they also have a wrapper:
the Function object type.
The Function object type
has two properties:
arguments, which contauins an array of
arguments passed to the function, and
caller, which refers to the function that called the current
function.
null.
function f() { return f.arguments[0] * f.arguments[1];
In this example the function implements a binary * (multiply).
When we introduce the constructor of the
Function() type we will actually show a way to create unnamed
functions, and you may encounter occasional circumstances in whic the body
of a function does not know how to refer to itself. If you encounter one of
these rare cases you can refer to the current function by passing the string
"this" to the eval() method (a method of the
Function object, as it is of all objects). For example to
refer to the caller property of the current
function, without explicitly naming it, we can use
eval("this").caller
The arguments[] Array
Example. JavaScript allows any number of argument values to be passed to any function, regardless of the number of argument names that appear in the function definition. Here's how a function can check for the correct number of arguments at run time:
function f(x, y, z) {
// first, check that the right # of arguments were passed
if (f.arguments.length != 3) {
alert("function f called with " + f.arguments.length +
"arguments, but it expects 3 arguments.");
}
// now do the actual function...
}
Here's a multi-argument max() function:
function max() {
var m = - Number.MAX_VALUE;
// loop through all the arguments, looking,
// for, and remembering, the biggest:
for (var i = 0; i < max.arguments.length; i++)
if (max.arguments[i] > m)
m = max.arguments[i];
// return the biggest
return m;
}
You can also write functions that have some named arguments,
followed by some unnamed arguments. The following examples shows
such a function: it is a constructor function that creates an array,
initializes a size property as specified by a named
argument len, and then initializes an arbitrary number
of elements, starting with element 1, of the array to the values of
of any additional elements.
function InitializedArray(len) {
this.size = len;
for (var i = 1; i < InitializedArray.arguments.length; i++)
this[i] = InitializedArray.arguments[i];
}
The caller Property
This is a reference to the function that called the current function.
The Function() Constructor
The Function() constructor provides a technique for defining
functions without using the function keyword. You can create a
new Function object with the Function() constructor
like this:
var f = new Function("x", "y", "return x * y; ");
This line of code creates a new function (wrapped within a new
Function object) that is equivalent (almost) to a function defined
with the syntax we're already familiar with:
function f(x, y) { return x * y; }
The Function() constructor expects any number of
string arguments. The last argument in the list becomes the body
of the function - it can contain arbitrary JavaScript statements,
separated from each other with semicolons. All other arguments to
the Function() conmstructor are strings that specify
the names of the arguments to the function being defined. If you
are defining a function that takes no arguments, then you simply
pass a single string - the function body - to the constructor.
There are a couple of reasons you might want to use
the Function() constructor. Recall that
the function keyword defines a variable,
just like the var does. So the first
reason to use the Function() constructor
is to avoid having to give your function a temporary
variable name when you are just going to immediately
assign it to an object property (making a method of that
object). For example, consider the following two lines of
code:
function tmp_area() {
return Math.PI * this.radius * this.radius;
}
Circle.area = tmp_area;
The Function() constructor allows us to do this in a
single step without creating the temporrary tmp_area
variable:
Circlea.area =
new Function("return Math.PI * this.radius * this.radius;");
Another reason you might want to use the Function() constructor is
to define temporary or "anonymous" functions that are never given a name.
The only difference between functions defined with the function
keyword and those defined with the Function() constructor has to
do with how they are printed. (Try it! Use document.write() or
alert()).
Function Properties
function f() { alert('hello world!'); }
f.i = 3;
This code creates a function f, and then assigns a property
i to it. Later, we can use this property just like any other:
var i = f.i + 2;
Function) itself. The following code demonstrates:
function f(x) {
var y = 3; // a local variable
return f.x + f.y;
// refer to the argument and
// variable as properties
}
If we invoke the function, we see that function arguments and local
variables really can be accessed as properties of the function itself:
However, if we try to read these properties ourselves, we will be unable to:result = f(2); // returns 5
What this means is that, like thetypeof f.x; // yields "undefined" typeof f.y // yields "undefined"
arguments() array
and the caller property, the local variable and argument
properties are only accessible while the function is running. When the
function returns, JavaScript deletes these properties.
function count() {
// counter is a static variable, defined below.
// Note that we use it just like a local variable.
alert("You've called me " + counter + " time(s).");
// Increment the static variable. This incremented
// value will be retained and will be used the next
// time we are called
counter++;
}
// To define the static variable, just set it as
// a property of the function: Note that the only
// shortcoming of this technique is that static
// variables can only be defined after they are
// used in the function
count.counter = 1;
Built-in Functions
JavScript also defines a number of built-in functions that are part of
the language: for example, ParseInt(), Math.sin() etc.
Practically the only difference between built-in functions and user-defined
functions is apparent when you try to print the value of a built-in function,
when the string "[native code]" appears, indicating
that the function itself is not implemented in JavaScript.
Event Handlers
An event-handler is a special-purpose function, one that is used only in client-side JavaScript. Event-handler functions are not usually invoked by your program, instead they are invoked by the web browser itself, whenever certain "events" occur. For example we can associate some JavaScript code with a button in an HTML form.
<form>
<input type="submit"
value="Click me!"
onClick="var sum = 1 + 2; alert(sum); ">
</form>
F. Objects
An object is a data type that contains named pieces of data. Each named datum is called a property. Each proerty has a name, and the object associates a value with each property name. A property value may be of any type. In effect, the properties of an object are variables within the "name space" created by the object.
You normally use the . operator to access
the value of an object's properties. The value on the left of the
. should be a reference to an object (usually
just the name of the variable that contains the object reference).
The value on the right of the . should be the
name of the property. This must be an identifier, not a string
or an exprssion. For example, you refer to the
property p in an object o with
o.p. Or, you refer to the property document
in the object parent with parent.document.
For example:
// Read a property value: w = image.width; // Set a property value: window.location = "http://www.cs.indiana.edu"; // Read one property and set it to another property image.src = parent.frames[1].location;
You can add a new property to an object simply by setting its value.
Thus, you might add a property to the object win with
code like the following:
This lines assigns the value ofwin.creator = self;
self to the
creator property of the object win.
If the creator property does not already exist, then it will
be created so that the value can be assigned to it.
If you attempt to read the value of a property that does not exist -- i.e., has never had a value assigned to it -- you will retrieve the special JavaScript udnefined value.
Once a property has been defined in an object, however, there is no way
to undefine it. You may set the value of a property to the special undefined
value, by assigning the value of an undefined property, but this changes the
value of the property without actually undefining it (that's why the
remove method in our shopping cart example creates a whole
new object that does not have the feature that we want removed
from the original object). You can demonstrate that the property stil
exists by a using a for/in loop to print out the name of all
defined properties:
for (prop in obj) property_list += prop + "\n"; alert(property_list);
As we saw briefly in one of the sections above
the new operator creates a new object. For example:
This syntax creates an "empty" object, one that has no properties defined. There are certain occasions in which you might want to start with an empty object of this sort, but in "object-oriented" programming, it is more common to work with objects that have a predefined set of properties. For example, you might want to define one or more objects that represent rectangles. In this case each rectangle object should have ao = new Object();
width property and a height property.
To create objects with properties such as width and
height already defined, we need to write a constructor
to create and initialize these properties in a new object. A constructor is
JavaScript function with three special features:
new operator
this
keyword, and it is responsible for performing appropriate initialization
for that new object.
return
statement, it should do so without a value to be returned. (In our examples
we are returning this and that's OK for the purpose of the
shopping cart).
// define the constructor
// Note how it initializes the object referred to by "this"
function Rectangle(w, h) {
this.width = w;
this.height = h;
}
// invoke the constructor to create two rectangle
// objects. Notice that we pass the width and height
// to the constructor, so it can initialize each new
// object appropriately.
rect1 = new Rectangle(2, 4);
rect2 = new Rectangle(8.5, 11);
Note how the constructor performs its initialization on the object referred
to by the this keyword. A constructor will generally perform
initialization based on the argument values that are passed to it. Some constructors
may also initialize other properties of a new object (setting them to constant values,
for example). Keep in mind that a constructor function simply initializes the
specified object; it does not have to return that object (Flanagan vs. Stein, I guess). Also notice how we define a "class" of objects simply by defining an appropriate constructor function - all objects created with that constructor will have the same properties.
constructor property
Starting with Navigator 3.0 all objects have a a constructor
property that refers to the constructor function that was used to create
the object. Since the constructor function determines the "class" of an
object, the constructor property specifies the "type" of any
given object. For example, you might use code like the following to
determine the type of an unknown object:
if ((typeof n == "object") &&
(n.constructor == Number))
// then do something with the Number object...
A method is nothing more than a JavaScript function that is invoked through an
object. Recall that functions are data values, and there is nothing special about the name
they are defined with -- a function can be assigned to any variable, or even to any property
of an object. If we have a function f, and an object o, then we can
defined a method named m with the following line:
Having defined the methodo.m = f;
m() of object o, we invoke it like this:
Or, ifo.m();
m() expects two arguments, we might invoke it like this:
Invokingo.m(x, x+2);
o.m() this way is the same as calling f(), except
for one point: when the function is invoked as a method, through the object o,
the this keyword will refer to that object within the body of the method. When
the same function object is invoked directly as f(), the this keyword
will not contain a meaningful value. (Variables in client-side JavaScript are all implicitly
properties of the current Window object, so invoking f() is equivalent to
invoking window.f(): the this keyword in both these
cases refers to the current window.
The discussion of the this keyword should begin to make it
clear why we use methods at all. Any function
that is used as a method is effectively passed a third argument --
the object through which it is invoked. Typically, a method
performs some sort of operation on that object, and the method
invocation syntax is a particularly elegant way to express the fact that a function
is operating on an object. Compare the following two lines of code:
The hypothetical methodo.m(x, y); f(o, x, y);
m() and function f() may
perform exactly the same operation on the object o, but the method
invocation syntax more clearly indicates the idea that it is the object o
that is the primary focus or target of the operation.
The typical usage of methods is more clearly illustrated through an example. The
example below returns to the Rectangle objects of the previous example
and how a method that operates on Rectangle objects can be defined and
invoked.
// This is a function. It uses the
// this keyword, so it doesn't make sense to invoke
// this function by itself; it needs instead be made
// a method of some object, some object that has
// "width" and "height" properties defined.
function compute_area() {
return this.width * this.height;
}
// Create a new Rectangle object, using the
// constructor defined earlier
var rect = new Rectangle(8.5, 11);
// Define a method by assigning the function
// to a property of the object
rect.area = compute_area;
// Invoke the new method like this:
a = rect.area(); // a = 8.5 * 11 = 93.5
There is a shortcoming that is evident in the example above: before you can
invoke the area() method for the rect object, you
must assign that method to a property of the object. While we can invoke the
area() method on the particular object named rect,
we can't invoke it on any other Rectangle objects without first
assigning the method to them. This quickly becomes tedious. The next example
defines additional rectangle methods and shows how they can
automatically be assigned to all Rectangle objects with a
constructor function.
// First, define some functions that will be used as methods
function Rectangle_area() {
return this.width * this.height;
}
function Rectangle_perimeter() {
return 2 * this.width + 2 * this.height;
}
function Rectangle_set_size(w, h) {
this.width = w; this.height = h;
}
function Rectangle_enlarge() {
this.width *= 2; this.height *= 2;
}
function Rectangle_shrink() {
this.width /= 2; this.height /= 2;
}
// Then define a constructor method for our
// Rectangle objects. The constructor initializes
// properties, and also assigns methods.
function Rectangle(w, h) {
// initialize object properties
this.width = w;
this.height = h;
// define methods for the object
this.area = Rectangle_area;
this.perimeter = Rectangle_perimeter;
this.set_size = Rectangle_set_size;
this.enlarge = Rectangle_enlarge;
this.shrink = Rectangle_shrink;
}
// Now, when we create a rectangle,
// we can immediately invoke methods
// on it:
r = new Rectangle(2, 2);
a = r.area();
r.englarge();
p = r.perimeter();
We've seen that a constructor function defines a "class" of objects in JavaScript -- all objects created with a given constructor will be initialized in the same way and will therefore have the same set of properties. These properties may include methods, for (as we've also seen) you can use a constructor function to assign a set of methods to each object that is a member of the class.
Since Navigator 3.0 there is yet another way to specify the methods, constants, and other properties that all objects in a class will support. The technique is to define the methods and other properties in a prototype object for the class. A prototype object is a special object, associated with the constructor function for a class, that has a very important feature: any properties defined by the prototype object of a class will appear as properties of every object of that class. This is true of properties that are added to the prototype both before and after the objects are defined. The properties of the prototype object of a class are shared by all objects of that class (i.e., objects do not get their own unique copy of the prototype properties, so memory usage is minimal).
The properties of the prototype object for a class can be read through all objects of that class, and, although they appear to be, they are not actually properties of those objects. There is a single copy of each prototype property, and this copy is shared by all objects in the class. When you read one of these properties of an object, you are reading that shared value from the prototype object. When you set the value of one of these properties for a particular object, on the other hand, you are actually creating a new property for that one object. From that point on, for that one one particular object, the newly created property "shadows," or hides, the shared property in the protoype object.
Because prototype properties are shared by all objects of a class, it only generally makes sense to use them to define properties that will be the same for all objects, within the class. This makes them ideal for defining methods. Other properties with constant values (such as mathematical constants) are also suitable for definition with prototype properties. If your class defines a property with a very commonly used default value, you might define this property, and the default value in a prototype object. Then the few objects that want to deviate from the default value can create their own private, unshared, copy of the property, defining their own nondefault property value.
After all this discussion of how prototype objects and their properties work,
we can now discuss where you can find prototype properties, and how they are created.
The prototype object defines methods and other constant properties for a class of
objects; classes of objects are defined by a common constructor; therefore,
the prototype object should be associated with the constructor function. This is
indeed the case. If we were to define a Circle constructor function to create
objects that represent circles, then the prototype object for this class would be
Circle.prototype, and we could define a constant that would be available to all
Circle objects like this:
The prototype object of a constructor is created automatically by JavaScript. In Navigator, it is created the first time the constructor is used with theCircle.prototype.pi = 3.141592
new operator.
What this means is that you must create at least one object of a class before you
can use the prototype object to assign methods and constants to objects of that
class. So, if we have defined a Circle() constructor, but not yet used it to create
any Circle objects, we'd define the constant property pi like this:
Properties objects and their properties can be quite confusing. The example below is a concrete example of how you can use prototypes to help you define a class of objects. In this example, we've switched from our Rectangle class to a new Circle class. The code defines a// First create and discard a dummy Circle object. // All this does is force the prorotype object to be created. new Circle(); // Now we can set properties in the prototype Circle.prototype.pi = 3.141592;
Circle class of objects, by first defining a Circle()
constructor method to initialize each individual object, and then by setting properties
on Circle.prototype to define methods, constants, and defaults shared by all
instances of the class.
// Define a constructor method for our class
// Use it to initialize properties that will be
// different for each individual circle object.
function Circle(x, y, r) {
this.x = x; // the X coordinate of the center of the circle
this.y = y; // the Y coordinate of the center of the circle
this.r = r; // the radius of the circle
}
// Create and discard an initial Circle object. Doing
// this forces the prototype object to be created
new Circle(0, 0, 0);
// Now define the constant; a property that will be
// shared by all circle objects. Actually, we could
// just use Math.PI, but we do it this way for the
// sake of example.
Circle.prototype.pi = 3.141592;
// Now define some functions that perform computations
// on circles. Note the use of the constant defined above
function Circle_circumference() {
return 2 * this.pi * this.r;
}
function Circle_area() {
return this.pi * this.r * this.r;
}
// Make these functions into methods of all Circle objects
// by setting them as properties of the prototype object.
Circle.prototype.circumference = Circle.circumference;
Circle.prototype.area = Circle_area;
// Now define a default property. Most Circle objects will
// share this default value, but some may override it by
// setting creating their own unshared copy of the property
Circle.prototype.url = "images/default_circle.gif";
// Now, create a circle object, and use the methods defined
// by the prototype object
c = new Circle(0.0, 0.0, 1.0);
a = c.area();
p = c.circumference();
Although JavaScript supports a data type we call an "object", the language's lack of string typing and a formal inheritance mechanism mean that it is not a truly object-oriented language. Still, JavaScript does a good job of simulating the features of object-oriented languages liek Java and C++. For example, we've been using the term "class" in the last few sections of this chapter, despite the fact that JavaScript does not officially define or support classes. This section will explore some of the parallels between JavaScript and the true object-oriented features of Java and C++.
We start by defining some basic terminology. An object, as we've already seen, is a data structure that "contains" various pieces of named data, and may also contain various methods to operate on those pieces of data, An object groups related data values and methods into a single convenient package, which generally makes programming easier, by increasing the modularity and reusability of code. Objects in JavaScript may have any number of properties, and properties may be added to an object dynamically. This is not the case in strictly typed languages like Java and C++ -- in those languages, each object has a predefined set of properties, (or fields, as they are often called) and each property contains a value of a predefined type. So when we are using JavaScript objects to simulate onject-oriented programming techniques, we will generally define in advance the set of properties that each object will have, and the type of data that each property will hold.
In Java and C++, a class is a thing that defines the structure of an object. It is the class that specifies exactly what fields an object contains, and what types of data each holds. It is also the class that defines the methods that operate on an object. JavaScript does not have a formal notion of a class, but, as we have seen, it approximates classes with its constructors. A constructor function can create a standard set of properties for each object it initializes.
In both JavaScript and true object-oriented languages. there may be multiple objects of the same class. We often say that an object is an instance of its class. Thus, there may be many instances of any class. Sometimes we use the term instantiate to describe the process of creating an object (an instance of a class).
In Java, it is a common programming convention to name classes with an initial capital
letter, and to name objects with lower case letters. This helps to keep classes and objects
distinct from each other in our code, and this is a useful convention to follow in JavaScript
programming as well. In previous sections, for example, we've defined the Circle
and Rectangle "classes," and have created instances of those classes named
c and rect.
The fields defined by a Java class may be of four basic types:
An "instance variable" is a variable of an instance, or object. It is a variable
contained in an object. Each object has its own separate copy of this variable; if
there are ten objects of a given class, then there are ten copies of this variable. In
our Circle class, for example, every circle object has a r property that
specifies the radius of the circle. In this case r is an instance
variable. Since each object has its own copy of instance variables, these variables are accessed
through individual objects. If c is an object that is an instance of the Circle class, for
example, then we refer to its radius as:
By default, any object property in JavaScript is an instance variable, but to truly simulate object-oriented programming, we will say that instance variables in JavaScript are those properties that are created and/or initialized in an object by the constructor function.c.r
An "instance method" is much like an "instance variable" except that it is a method rather
than a data value. (In Java, functions and methods are not data types, as
they are in JavaScript, so this distinction is more clear). Instance methods are
invoked on a particular "instance" or object. The area() method of our
Circle class is an instance method. It is invoked on a Circle object c
like this:
Instance methods use thea = c.area();
this keyword to refer to the object
or instance they are operating on.
An instance method can be invoked for any instance of a
class, but this does not mean that each object contains its own private
copy of the method, as it does its instance variables.
Instead, each instance method
is shared by all instances of a class. In JavaScript,
we define an instance method
for a class by setting a property
in the constructor's prototype object to a function value.
This way, all objects created by that constructor share a reference
to the function, and can invoke it using the method invocation syntax shown above.
A "class" or "static" variable in Java is a variable that is associated with a class
itself, rather than with each instance of a class. No matter how many instances of the
class are created, there is only one copy of each class variable. Just as instance
variables are accessed through an instance of a class, class variables are accessed
through the class itself. Number.MAX_VALUE is
an example of a class variable in JavaScript -- the MAX_VALUE
property is accessed through the Number class. Because there is
only one copy of each class variable, class variables are essentially global
variables. What is nice about them, however, is that by being associated with
the class, they have a logical niche, a position in the JavaScript name space,
where they are not likely to be overwritten by other variables with the same
name. As is probably clear, we simulate a class variable in JavaScript simply
by defining a property of the constructor function itself. For example, to create
a class variable Circle.PI to store the mathematical constant, often
used with circles, we could do the following:
Finally, we come to class methods. A "class" or "static" method is a method associated with a class rather than with an instance of a class. Class methods are invoked through the class, rather than through a particular instance of the class.Circle.PI = 3.14;
Math.sqrt(), Math.sin(), and other
methods of the Math object are class methods. Because class methods
are not invoked through a particular object, they cannot use the this
keyword -- this refers to the object that an instance
method is invoked for. Like class variables, class methods are "global". Because
they do not operate on a particular object, static methods can often more easily
be thought of as functions that happen to be invoked through a class. Again,
associating these functions with a class gives them a convenient niche in the
JavaScript name space, and prevents "name space collisions" from occurring in
case some other class happens to define a function with the same name. To
define a class method in JavaScript, we simply set the appropriate function
as a property of the constructor. The next example is a re-implementation of our Circle class that contains example of each of these four basic types of fields.
function Circle(radius) {
// the constructor defines the class itself
this.r = radius;
}
// Circle.PI is a class variable --
// it is a property of the constructor function
Circle.PI = 3.141592
// Here is a function that computes the circle area
function Circle_area() {
return Circle.PI * this.r * this.r;
}
// Here we make the function into an instance method
// by assigning it to the prototype object of the
// constructor. Remember that we have to create
// and discard one object before the prototype
// object exists.
new Circle(0);
Circle.prototype.area = Circle_area;
// Here's another function. It takes two circle
// objects as arguments and returns the one that
// is larger (has the larger radius)
function Circle_max(a, b) {
if (a.r > b.r) return a;
else return b;
}
// Since this function compares two objects, it does
// not make sense as an instance method operating on
// a single circle object. But we don't want it to
// be a standalone function either, so we make it int
// a class method by assigning it to the constructor
// function:
Circle.max = Circle_max;
// Here is some code that sues each of these fields:
c = new Circle(1.0);
// create an instance of the Circle class
c.r = 2.2;
// set the r instance variable
a = c.area();
// invoke the area() instance method
x = Math.exp(Circle.PI);
// use the PI class variable in our own computation
d = new Circle(1.2);
// create another Circle instance
bigger = Circle.max(c,d);
// use the max() class method
We've seen the . operator
used to access the properties of an object. It is also
possible to use the [] operator, more commonly used with
arrays, to access these properties. This the following two JavaScript
expressions have the same value:
The important difference to note between these two syntaxes is that in the first, the property name is an identifier, and in the second, the property name is a string. We'll see why this is so important below.object.property object["property"]
In C, C++, Java, and similar strongly typed
languages an object can have only a
fixed number of properties (or "fields", as they are often
called), and the names of these properties must be defined in advance. Since
JavaScript is a loosely typed language, this rule does not apply -- a program can
create any number of properties in any object. When you use the .
operator to access a property of an object, however, the name of the property is
expressed as an identifier, and identifiers must be
"hardcoded" into your JavaScript program. That is, identifiers are not a JavaScript
data type; they must be typed litterally into a JavaScript program, and cannot be
manipulated by the program.
On the other hand, when you access a property of an object
with the [] array notation, the name of the property
is expressed as a string. Strings are JavaScript data types, and
they can be manipulated and created while the program is running.
So, for example, you could write the following code in JavaScript:
var addr = "";
for (i = 0; i < 4; i++) {
addr += customer["address" + i];
}
This code fragment reads and concatenates
the properties address0,
address1, address2,
and address3 of the
,code>customer object.
The code fragment above demonstrates the flexibility ofm using array notation to
access properties of an object with string expressions. We cxould have actually
written that example using the . notation, but there are cases
for which only the array notation will do. Suppose, for example, that you are
writing a program that uses network resources to compute the current value of the
user's stock market investments. The program allows the user to type in the name of each stock they own,
and also the the number of shares of each stock. You might use an object named portfolio
to hold this information. The object would have one property for each stock; the name of the
property would be the name of the stock, and the property value would be the number
of shares of that stock. So, for example, is a user held 50 shares of stock in
Netscape Communications Corp., then the portfolio.nscp property would have the value
50.
One part of this program would be a loop that prompts the user to enter the name of a stock they own, and then asks them to enter the number of shares they own of that stock. Inside the loop, you'd have code something like the following:
Since the user enters stock names at run-time, there is no way that you can know the property names ahead of time. Since you can't know the property names when you write the program, there is no way you can use thestock_name = get_stock_name_from_user(); shares = get_number_of_shares(); portfolio[stock_name] = shares;
.
operator to access the properties of the portfolio object. You can use the
[] operator, however, because it uses a string value (which is dynamic and can change at
run-time), rather than an identifier (which is static and must be hard-coded in the program), to
name the property.
When an object is used this fashion, it is often called an
associative array -- a data structure that allows you to dynamically associate arbitrary data
values with arbitrary strings. JavaScript objects are actually implemented internally
as associative arrays. The . notation for accessing properties makes them seem like
the static objects of C++ and Java, and they work perfectly well in that capacity. But they also have
the very powerful ability to associate values with arbitrary strings. In this
respect, JavaScript objects are much more like Perl arras than like C++ or Java objects.
An early section on Statements introduced the for/in loop. The
real power of this JavaScript statement becomes clear when we consider its use with an associative
array. To return to the stock portfolio example, we might use code that looked like the following
after the user had entered her portfolio and we were computing its total current value:
value = 0;
for (stock_name in portfolio) {
// for each stock in the protfolio
value += get_share_value(stock_name) *
portfolio[stock_name];
}
We couldn't write this code without the for/in loop, because
the names of the stocks aren't known in advance, and this is the only way to extract
those property names from the associative array (i.e., JavaScript object) named
portfolio.
For any object in JavaScript, there are three special methods that control the way the object is manipulated. Each of these objects is automatically invoked by JavaScript to manipulate the object in some way. By providing a custom definition of the method, you can control the way an object is manipulated.
toString() Method
This method is invoked to convert the object to a string.
It is an excellent candidate for inclusion in a prototype
object when defining a class of JavaScript objects. We
might write and register a toString() method
for our Circle class as follows:
function Circle_toString() {
return "[Circle of radius " + this.r +
", centered at (" + this.x +
", " + this.y + ").]";
}
Circle.prototype.toString = Circle_toString();
valueOf() Method This method is called to convert the object to a number or to a nonobject type.
assign() Method
This method is invoked to assign a value to the object. It is
invoked when the object appears on the left-hand side of an assignment.
The purpose of the method is in some fashion to
assign the value passed as an argument to the object
referred to by this. One use of this method is
to implement assignment with side effects. Another one is
to create read-only objects (by defining a custom
assign() method that does
not do anything). Finaly you could also use it
to change the way assignment is done (i.e., from
by reference to by value, etc).
The last section documented the JavaScript object type - a data structure that contains named pieces of data. This section documents the array type - a data structure that contains numbered pieces of data. (Note that the arrays discussed here are not the same thing as the "associative arrays" described in the previous section althouhg as we will see there is not as much difference among associative arrays, the "regular" arrays described here, and objects as it might first appear). Here now is a summary of this section, its main ideas:
Array.join() converts
an array, and all its elements, into a single string
Array.reverse() can be used to reverse
the order of elements in the array
Array.sort() can
be used to sort the elements of an array
Array() constructor, and arrays created with this
constructor have a length property that is automatically
maintained so that it always contains a value one greater than the largest
index of the array.
Array() constructor but you can write your
own. Also, there is no automatically maintained
length property, but it is common to
reserve element 0 of an array for a
size property (which you update yourself
as the array grows).
length or size property.
An array is a data type that contains or stores numbered pieces of data. Each numbered datum is called an element of the array, and the number assigned to an element is called its index. Because JavaScript is an untyped language an element of an array may be of any type, and different elements of the same array may be of different types. Array elements can contain other arrays, which allows you to create data structures that are arrays as arrays.
You can access an element of an array with the [] operator.
Inside the brackets you should have a non-negative number. You can use this
syntax to both read and write the value of an element of an array.
In JavaScript arrays can have any number of elements and you can change the number of elements at any time. To add a new element to an array simply assign a value to it.
Arrays in JavaScript are sparse. This means that when you execute the following lines of code
JavaScript allocates memory only for two elements.a[0] = 1; a[10000] = "this is elem. 10,000";
Can't be done (with a few minor exceptions)
You van approximate them either as arrays of arrays or associative arrays.
In the first case you just use the [] operator twice. In the
second you need to make a string out of the 3 indices, a unique key. For
example:
function index3(arr, x, y, z) {
return arr[x + "," + y + "," + z];
}
You've seen this thing in Perl.
Objects and arrays are the same thing. You can verify this with the
typeof operator -- use it with any array or with any
object and it returns the string "object". Because arrays and objects
are the same thing, any object can have numericall indexed array elements,
and any array can have named properties:
o.prop = "property1"; o[1] = "element1"; a[3] = a[2] + a[1]; a.size = 3;
Since they are the same thing as objects they can be
created in the same way, with the new operator.
You've seen this in the shopping cart example and in the book.
In recent versions of Navigator there is a predefined
Array() constructor function that you can
use to create arrays. You can use this constructor in
three different ways:
a = new Array(); -- with no arguments
a = new Array(10); -- with an initial length
Elements are assigned to the array starting with elementa = new Array(5, 4, 3, 2, 1, "test");
0.
length property of arrays post-Navigator 3.0
is not read-only and once you set it to a smaller value the array
shortens accordingly.
length Property and Sparse Arrays
Client-side JavaScript has quite a few built-in arrays.
For example the elements[] array of the Form
object and many others.
A348/A548.