Приглашаем посетить
Паустовский (paustovskiy-lit.ru)

Advanced JavaScript Techniques

Advanced JavaScript Techniques

This section introduces some of the advanced JavaScript concepts used throughout this book. This section is not meant to teach JavaScript, but rather to ensure familiarity with some of the more powerful but less common aspects of the language.

Adding Properties to Objects

Arrays and objects in JavaScript provide two techniques for accessing their contents: directly referencing the contents as a property using dot (.) notation, or referencing an index into the array using bracket ([index]) notation. An index into a JavaScript array can be a string value that represents the property name. Dot notation allows direct access to a property when the property name is known in advance. When the property being accessed needs to be a variable, it can be accessed late-bound using a string identifier:

<SCRIPT LANGUAGE="JavaScript">
   var prop = "title";
   alert(document.title); // Access the title using dot notation.
   alert(document[prop]); // Access the property referenced by
                          // the prop variable.

   // Arrays and built-in objects work alike.
   var ar = new Array;
   ar.myProperty = "Demo";
   alert(ar.myProperty);
   alert(ar["myProperty"]);
</SCRIPT>

Objects in JavaScript are unique in their ability to automatically expand. You can add a new property to an object simply by assigning it a value. This feature comes at the expense of making debugging more difficult.

JavaScript is case sensitive, as demonstrated here:

<SCRIPT LANGUAGE="JavaScript">
   alert(document.title);   // Output the title of the document.
   document.Title = "Not the real title"; // Add a Title property.
   alert(document.Title);   // Output the new Title property.
</SCRIPT>

In this example, the two alert statements are actually different. Furthermore, the second line generates no error; instead, Title is added as a property of document. Therefore, you must be careful when writing JavaScript code. Debugging a large amount of script may be quite difficult.


NOTE: Internet Explorer 3.0 did not enforce these case-sensitivity rules. Internet Explorer 4.0 and all Netscape Navigator releases enforce strict case sensitivity.

To help alleviate this debugging problem, Internet Explorer 4.0 exposes a property on the document, the expando property, that can be used to disable the implicit property addition feature of JavaScript, as shown in this code:

<SCRIPT LANGUAGE="JavaScript">
   /* Internet Explorer 4.0 supports the ability to turn off the
      associative array nature of built-in objects. */
   document.expando = false;
   document.Title = "Not the real title";  // Error--no such property
</SCRIPT>

The expando property does not disable the explicit addition of properties to the window through variable declarations, but it does disable the implicit addition, as in the following example:

<SCRIPT LANGUAGE="JavaScript">
   document.expando = false;
   var x = 0;        // No error
   alert(window.x);  // x explicitly added.
   window.y = 10;    // Error--no y property
</SCRIPT>


NOTE: Internet Explorer 4.0 is the first browser that supports the expando property to control the associative array characteristic of objects. Netscape Navigator 4.0 and earlier versions of Internet Explorer do not recognize this property. When the expando property is not recognized, referencing it will automatically cause it to be added to the document object.

Because any object may contain any number of properties, JavaScript exposes a convenient operator for accessing them. With a for…in loop, you can execute a statement for each exposed property in an object without knowing what the properties are. For example, the following code outputs all the properties exposed on the window object:

<SCRIPT LANGUAGE="JavaScript">
   // Display an alert with all the properties of the window and 
   // their values.
   var sProps = "Window Properties\n";
   for (props in window)
      sProps += props + ": " + window[props] + "\n";
   alert(sProps);
</SCRIPT>

Function Pointers

Any function can be assigned to and manipulated as a property. This fact allows a function to be dynamically added as a method that can be invoked or to be dynamically associated with an event handler.

Function pointers are extremely powerful in that they let you reuse functions as methods of an object, as in this example:

<SCRIPT LANGUAGE="JavaScript">
   // Define a simple function named test.
   function test() {
      alert("Function has been invoked.");
   }

   // Assign the onclick handler to be test.
   // This causes the function test to be called when the document
   // is clicked.
   document.onclick = test;  
</SCRIPT>

In addition, when a function is called, it has access to an arguments array containing any parameters that were passed into the function. JavaScript automatically populates the arguments array at the time the function is invoked. The following code demonstrates how a function can access the arguments array:

<SCRIPT LANGUAGE="JavaScript">
   function testArgs() {
      /* A function in JavaScript can access an arguments array.
         This array contains all parameters that were passed into the
         function. */
      alert(arguments.length + " arguments");  // Output the number of
                                               // arguments.
      // Output each argument.
      for (var i = 0; i < arguments.length; i++)
         alert("argument " + i + " - " + arguments[i]);
   }
   testArgs(1, 2, 3, 4);  // Call testArgs with four arguments.
</SCRIPT>

The arguments array allows a function to be written to which a variable number of arguments can be passed. Based on the arguments that were passed, different actions can occur. A simple demonstration of where to use this capability is a summation routine:

<SCRIPT LANGUAGE="JavaScript">
   function Sum() {
      // Sum up all the arguments passed in and return the result.
      var intSum = 0;
      for (var intLoop = 0; intLoop < arguments.length; intLoop++)
         intSum += arguments[intLoop];
      return intSum;
   }

   alert(Sum(1, 1, 1, 2)); // Add the four values.
</SCRIPT>

Functions can also be dynamically created using the new operator. The new operator allows a new function routine to be constructed on the fly. A function is created as follows:

var functionname = new Function(args1, , argsn, body);

Any number of args may be supplied, including 0. The last argument to the Function constructor is always the code to execute. For example, the following code creates a simple function that returns the difference between two numbers:

var Difference = new Function("x","y","return x - y;");

This example is contrived, as it would be simpler to either perform the operation directly or encapsulate the code in a real function. The value in this code is that a temporary function can be created and discarded, or the code provides a simple way to dynamically construct a function call. Creating functions to dynamically create event handlers is demonstrated in several examples in this book.

Checking for Support

JavaScript offers a flexible way to test whether a particular property or method is supported by the browser. This technique can be used to predetermine whether the code is going to succeed and possibly to run alternative code if the feature is unavailable. For example, the following code checks whether the all collection on the document is supported:

<SCRIPT LANGUAGE="JavaScript">
   if (null == document.all) {
      // The all collection is not supported; run alternative code.
   }
   else {
      // Do something with the all collection.
   }
</SCRIPT>

Property and Function Naming Conventions

The ability to add variables and functions to any object in JavaScript is very powerful. However, with this power comes risk as Dynamic HTML evolves. Every time a developer dynamically adds a property to an object, a potential conflict is created between that property and a future enhancement to the object model. Here are a few guidelines that can minimize the risk of future conflicts:

Perhaps the best technique for avoiding any conflict is to add only a single member object to any built-in object and then add all the new custom members to this object. This technique isolates the potential conflict to a single property, but it requires a little forethought: you must predefine the single property to ensure that no syntax errors are generated in the code. Here is an example of this technique being used on the window object, in which all custom members are added to a property named _Custom:

<SCRIPT LANGUAGE="JavaScript">
   // Before using the _Custom object, initialize it as a property of 
   // the window object.
   if (null == window._Custom)
      window._Custom = new Object;

   // Add properties to _Custom.
   window._Custom.special = true;
   window._Custom.top = self;
</SCRIPT>

The initializing statement is necessary before any properties can be added to the _Custom object because JavaScript can add only one member to an object at a time. If _Custom was not first initialized, an error would have occurred when the special property was accessed.

[Содержание]