CSCI A348/A548

Lecture Notes 13-14

Fall 1999


Core JavaScript (Part I and II)

A. Lexical Structure

  1. Case. JavaScript is a case-sensitive language. Note that HTML is not case-sensitive, which, because its close association with JavaScript, can be confusing.

  2. Whitespace. JavaScript ignores spaces. tabs, and newlines that appear between "tokens" in programs, except those that are part of string constants. A "token" is a keyword, variable name, number, function name, or some other place where you would obviously not want to insert a space or a line break. If you place a space or tab or newline within a token, you obviously break it up into two tokens. Because you can use spaces, tabs, and newlines freely in your program (except in strings or tokens) you are free to format and indent your programs in a consistent way that makes them easier to read.

  3. Semicolons. Simple statements in JavaScript are generally followed by a semicolon character which serves to separate them from the following statement.

  4. Comments. In JavaScript any text between a // and the end of a line is treated as a comment. Also, any text between the characters /* and */ is treated as a comment.

  5. Literals. A literal is a data value that appears directly in a program. In JavaScript these are:

  6. Identifiers. An identifier is a name used to refer to something else: a variable or function name. First character must be a letter or an underscore. Subsequent characters may be any letter or digit or an underscore.

  7. Reserved Words. There are a number of "reserved words" in JavaScript. They are words that you cannot use as identifiers. For example: if, while, break and so on. For a complete list check the reference pages.

    B. Variables and Data Types

    1. Variables. A variable is a name associated with a data value; we say that the variable "stores" or "contains" the value. Variables provide a way to manipulate values by name. Since the value associated with a name may vary the names are called variables.

    2. Variable Declaration. Although it is often unnecessary it is good programming style to declare variable before using them. You do this with the var keyword:
      var i; 
      var sum;
    3. Untyped Variables. JavaScript is an untyped language: variables can hold values of any data type. For example the following is legal:
      i = 10;
      i = "ten"; 
      For this reason variable declaration in JavaScript (with 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.

    4. Numbers. All numbers in JavaScript are represented as floating-point values.

    5. Arithmetic Operators. The language provides infix operators such as: +, -, *, and /.

    6. Mathematical Functions. More complex mathematical operations are stored as properties of a single object named 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); 
    7. Special Numeric Values. When a floating-point value becomes larger than the largest representable type, the result is a special infinity value, which JavaScript prints as 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.

    8. Strings. To concatenate string you use the + 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); 
    9. Boolean Values. true or false, usually as the result of comparisons in our programs. Used in if, while, and various other conditional control structures.

    10. Functions. A function is a piece of JavaScript code that is defined once in a program and can be executed or invoked many times by the program. JavaScript functions can be passed arguments or parameters that specify the value or values that the function is to operate upon, and can return values. Functions are defined in JavaScript with code like the following:
      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:
      y = square(x);
      click(); 
      y = sin(x); 
      compute_distance(x1, y1, z1, x2, y2, z2);
      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.

    11. Objects. An object is a collection of named pieces of data. The named pieces are usually referred to as properties of the object. Sometimes they are also called "fields". To refer to a property of an object we refer to the object and follow this reference with a period and follow the period with the name of the property. For example, if an object named image has properties named width and height we can refer to those properties like this:
      image.width
      image.height
      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:
      document.myform.button
      which refers to the 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"]
    12. Arrays. An array is a collection of data values, just as an object is. While each data value contained in an object has a name, each data value in an array has a number or index. Array indexing is like in Perl or C. Arrays may contain any type of JavaScript data, including references to other arrays or to objects or functions. So, for example, the JavaScript code:
      document.images[1].width
      refers to the 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.

    13. Null. Technically when a variable holds the value null you know it does not contain a valid object or array, number, string, Boolean or function.

    14. Undefined. The "undefined" value is returned when you use a variable that doesn't exist, or a variable that has been declared, but never had a value assigned to it, or an object property that doesn't exist. Unlike the 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:

      1. creates an object representing the current date and time
      2. creates a Date object representing Christmas '96
      3. changes the date to the next Christmas
      4. prints a string that represents the current date

    15. Data Type Wrapper Objects. Like in Java each primitive data type has a corresponding object type defined for it. These object types are known as "wrappers" - they contain the same primitive data value, but also define the properties and methods that we use to manipulate that data. This will be more useful for String and Function objects, and less useful for Numbers and Strings.

    C. Expressions and Operators

    1. Expressions. An expression is a "phrase" of JavaScript that the JavaScript interpreter can evaluate to produce a value. Simple expressions are constants or variable names. More complex expressions can be created by combining simple (or complex) expressions with the help of operators.

    2. Operator Overview.

      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

    3. Number of Operands. In general there are three types of operators: most JavaScript operators are binary operators that combine two expressions into a single, more complex expression. That is, they operate on two operands. JavaScript also supports a number of unary operators which convert a single expression into a single more complex expression. Finally, JavaScript supports one ternary operator ?: which combines the value of three expressions into a single expression.

    4. Type of Operands. When constructing JavaScript expressions you must pay attention to the data types that are being passed to operators and to the data types that are returned. Different operators expect their operands' expressions to evaluate to values of a certain data type. For example it is not possible to multiply (*) strings. Note however that JavaScript tries to convert expressions to the appropriate type whenever possible so an expression like
      "3" * "5"
      is legal although
      "a" * "b"
      isn't. Furthermore some operators behave differently depending on the type of the operands. Most notably the + operator adds numeric operands but concatenates string operands.

    5. Operator Precedence. In the table above the column labeled Prec. specifies the precedence of each operator. Operator precedence controls the order in which operations are performed. Operators with lower numbers in this column will be performed before those with a higher number. For example in the expression
      w = x + y * z;
      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 (=) 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.

    6. Operator Associativity. The column labeled Assoc. in the table above specifies the associativity of each operator. A value of 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:
      w = x + y + z;
      is essentially equivalent to:
      w = ((x + y) + z);
      since + is a binary operator with left to roght associativity.

      We now look again at the operators, from a different point of view.

    7. Arithmetic Operators: addition (+), subtraction (-), multiplication (*), division (/), modulo (%), unary negation (-), increment (++), decrement (--) you are already familiar with them.

    8. Comparison Operators. These are operators that compare values of various types and return a Boolean value (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:
      1. Equality (==) 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 (=).
      2. Inequality (!=) is an operator that tests for the opposite of ==
      3. Less than (<), greater than (>), less than or equal (<=), greater than or equal (>=) are operators for numbers.

    9. String Operators + concatenates two string operands. <, <=, > and >= compare two strings to determine what order they fall in. The comparison uses alphabetical order.

    10. Logical Operators The logical operators: and (&&), or (||), not (!), expect their operands to be Boolean values and they perform "Boolean algebra" on them.

    11. Bitwise Operators If you are not familiar with binary numbers and the binary representation of decimal integers you probably have no immediate need for this kind of operators. Their name appears in the table above in the appropriate columns.

    12. Assignment Operators The operator = is used in JavaScript to assign a value to a variable, for example:
      i = 0;
      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, = 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:
      (a = b) == 0
      If you do this, be sure you are clear on the difference between = and ==.

      Also, because of it being an operator with left to right associativity you can have multiple assignnment operators such as:

      i = j = k = 0;
      to assign a single value to multiple variables.

      JavaScript also supports a number of assignment operators that provide a shortcut by combining assignment with some other operation. In general

      a = op= b;
      where op is an operator is equivalent to:
      a = a op b;
      where op is one of the following: +, -, *, /, %, <<, >>, >>>, &, |, ^

    13. Miscellaneous Operators

      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:

      x > 0 ? x * y : - x * y; 
      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 is 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

      new constructor
      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 the 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:

      i = 0, j = 1, k = 2; 
      is equivalent to the three assignments one after another. This is of limited use, most often in 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:

      frames[1]
      document.forms[i + j]
      document.forms[i].elements[j++]
      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:
      document["lastModified"]
      frames[0]['length']
      data["val" + i]
      The [] 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.

    1. Expression Statements

      The simplest kind of statements in JavaScript are expressions that have side effects. One major category of these are assignment statements. For example:

      s = "Hello " + name;
      i *= 3;
      Relating to assignment statements are the increment and decrement operators ++ and --. These have the side effect of changing a valriable value, just as if an assignment had been performed:
      counter++;
      Function calls are another major category of expression statements.

    2. Compound Statements

      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.

    3. The if statement. This is the fundamental "control statement" that allows JavaScript to "make decisions". It has two forms:
      if (expression) 
        statement
      and
      if (expression) 
        statement1
      else 
        statement2
      When you have nested 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"); 

    4. The while loop. Here's an example while loop:
      count = 0; 
      while (count < 10) {
        document.write(count + "<br>"); 
        count++; 
      }

    5. The for loop. The syntax is
      for(initialize ; test ; increment) 
        statement
      and its while equivalent is:
      initialize ;
      while (test) {
        statement
        increment;
      }

    6. The 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.

    7. The 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; 
      }

    8. The 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]; 
      } 

    9. The 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:

      frames[1].document.forms[0].address_field.value
      The with provides a way to simplify expressions like this one, and reduce your typing. It has the following syntax:
      with (object) 
        statement
      For example you migh use the with to simplify the following code:
      x = Math.sin(i * Math.PI / 20);
      y = Math.cos(i * Math.PI / 30); 
      Using with, you might write:
      with(Math) {
        x = sin(i * PI / 20);
        y = cos(i * PI / 30); 
      }

    10. The var statement provides a way to explicitly declare a variable or variables. Examples:
      var i;
      var j = 0;
      var x = 2.34, y = 4.12, r, theta;
      The 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; 

    11. The 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.

    12. The 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.

    13. The Empty Statement

      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:
      // initialize an array a
      for (i = 0; i < a.length; a[i++] = i) ;
      To make your code clear, it can be useful to comment your empty statements as such:
      for (i = 0; i < a.length; a[i++] = i) 
        /* empty */ ;

    E. Functions

    Functions are an important and complex part of the JavaScript language.

    1. Defining and Invoking Functions

      Functions are defined with the function keyword, followed by:

      • the name of the function
      • a comma-separated list of argument names in parentheses
      • the JavaScript statements that comprise the body of the function, contained within curly braces.

      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)); 

    2. Functions as Data Types

      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:

      • Defines a new variable named square.

      • Creates a new data value, of type function. This function value expects a single argument named x, and has a body that consists of a single statement:
        return x * x;
      • Assigns the newly created function value to the newly defined variable.

      When we consider function definition in this light, it becomes clear that the name of a function is really immaterial -- it is simply the name of a variable that holds the function. The function can be assigned to another variable, and will still work the same:
      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:
      o = new Object;
      o.sq = square;
      y = o.sq(16); // y equals 256 
      Functions don't even require names, as when we assign them to array elements:
      a = new Array(10); 
      a[0] = square;
      a[1] = 20;
      a[2] = a[0](a[1]); // a[2] contains 400 
      Note that the function invocation syntax in this last example looks strange, but is still a legal use of the JavaScript function call operator.

      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")); 
    3. The Function Object

      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.

      Two points:

      1. These properties are only defined while the function is being executed. If you try to access them from outside the function their value will be null.

      2. In order to refer to these properties from inside the function the function needs to be able to refer to itself. The only way (in JSv3.0) is by the name of the function. Example:

        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

        • Functions are objects. One of the interesting features of JavaScript functions is that you can assign properties to them. For example:
          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;

        • Functions arguments and variables are properties In all versions of JavaScript, global variables are actually properties of some top-level object. In client-side JavaScript this top-level object is the browser window or frame that contains the JavaScript code. This raises the obvious question: if global variables are properties of an object, what are local function variables? It would make sense that they, too, are properties of some object. The only obvious object is the function (or 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:
          result = f(2); // returns 5
          However, if we try to read these properties ourselves, we will be unable to:
          typeof f.x; // yields "undefined" 
          typeof f.y  // yields "undefined" 
          What this means is that, like the 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 properties simulate static variables. JavaScript does not support static variables directly, but it turns out that we can simulate them with function properties. The example below shows a function that uses a "static" variable to keep track of how many times it has been called.

          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

      1. Object Properties

        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.

        • Reading and Writing Object Properties

          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;

        • Defining New Object Properties

          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:

          win.creator = self;
          This lines assigns the value of 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.

        • Undefined Object Properties

          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); 

      2. Creating New Objects with Constructors

        As we saw briefly in one of the sections above the new operator creates a new object. For example:

        o = new Object();
        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 a 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:

        • It is invoked through the new operator

        • It is pased a reference to a newly created, "empty" object as the value of the special this keyword, and it is responsible for performing appropriate initialization for that new object.

        • It should not return a value; if it uses the 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).

        The next example shows how the constructor function for a rectangle object might be defined and invoked:
        // 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.

        • The 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...

      3. Methods

        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:

        o.m = f;
        Having defined the method m() of object o, we invoke it like this:
        o.m();
        Or, if m() expects two arguments, we might invoke it like this:
        o.m(x, x+2);
        Invoking 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:

        o.m(x, y);
        f(o, x, y);
        The hypothetical method 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(); 
        

      4. Object Prototypes

        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:

        Circle.prototype.pi = 3.141592
        The prototype object of a constructor is created automatically by JavaScript. In Navigator, it is created the first time the constructor is used with the 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:
        // 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;
        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 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();

      5. Classes in JavaScript

        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:

        1. "instance" variables
        2. "instance" methods
        3. "static" or "class" variables, and
        4. "static" or "class" methods
        The paragraphs below explain the differences between these types of fields, and show how they are simulated in JavaScript.

        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:

        c.r
        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.

        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:

        a = c.area();
        Instance methods use the 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:
        Circle.PI = 3.14;
        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. 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 
        

      6. Objects as Associative Arrays

        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:

        object.property
        object["property"]
        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.

        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:

        stock_name = get_stock_name_from_user();
        shares = get_number_of_shares();
        portfolio[stock_name] = shares;
        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 the . 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.

      7. Special Object Methods

        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.

        • The 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();

        • The valueOf() Method

          This method is called to convert the object to a number or to a nonobject type.

        • The 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).

      G. Arrays

      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:

      • Arrays and objects are the same thing in JavaScript. Any object can have array elements, and any array can have object properties.
      • Since Navigator 3.0 there are three methods that can be used to manipulate arrays:
        • 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
      • In more recent versions of Navigator array elements and object properties do not overlap and cannot overwrite each other. There is an 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.
      • In older versions of Navigator objewct properties and array elements overlap; when you create a new property it is as if you added a new array element one higher that the highest existing element. There is no built-in 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).
      • For many algorithms the size of an array is maintained in a variable externally to an array and there is no need for a length or size property.
      • All arrays in JavaScript are (in fact) implemented as associative arrays, and can be "sparse"
      1. Array Elements

        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.

        1. Reading and Writing Array Elements

          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.

        2. Adding New Elements to 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

          a[0] = 1;
          a[10000] = "this is elem. 10,000";
          JavaScript allocates memory only for two elements.

        3. Removing Elements from an Array

          Can't be done (with a few minor exceptions)

      2. Multidimensional Arrays

        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.

      3. Array/Object Dual Nature

        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;

      4. Creating Arrays

        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
        • by initializeng the first elements:
          a = new Array(5, 4, 3, 2, 1, "test"); 
          Elements are assigned to the array starting with element 0.

      5. Array Length Property

          The 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.

        1. The length Property and Sparse Arrays

      6. Array Methods

      7. Built-in Array

        Client-side JavaScript has quite a few built-in arrays. For example the elements[] array of the Form object and many others.

      H. Miscellaneous (or Further Topics in JavaScript)

      1. Automatic Data Type Conversion

      2. Explicit Data Type Conversion

      3. By Value vs By Reference


      Last updated: October 19, 1999 by Adrian German