Home arrow JavaScript arrow Page 4 - More on Variables, Functions, and Flow Control
JAVASCRIPT

More on Variables, Functions, and Flow Control


In this second part of a two-part series, you'll learn how to delay a function call, handle script errors gracefully, and more. It is excerpted from chapter 4 of the JavaScript & DHTML Cookbook, Second Edition, written by Danny Goodman (O'Reilly, 2008; ISBN: 0596514085). Copyright 2008 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Author Info:
By: O'Reilly Media
Rating: 5 stars5 stars5 stars5 stars5 stars / 4
December 14, 2009
TABLE OF CONTENTS:
  1. · More on Variables, Functions, and Flow Control
  2. · 4.6 Branching Execution Based on Conditions
  3. · 4.7 Handling Script Errors Gracefully
  4. · 4.8 Improving Script Performance

print this article
SEARCH DEVARTICLES

More on Variables, Functions, and Flow Control - 4.8 Improving Script Performance
(Page 4 of 4 )

Problem

You want to speed up a sluggish script.

Solution

When swallowing small doses of code, JavaScript interpreters tend to process data speedily. But if you throw a ton of complex and deeply nested code at a browser, you may notice some latency, even after all the data has been downloaded in the browser.

Here are a handful of useful tips to help you unclog potential processing bottlenecks in your code:

  • Avoid using the eval() function.
  • Avoid the with construction.
  • Minimize repetitive expression evaluation.
  • Use simulated hash tables for lookups in large arrays of objects.

  • Avoid excessive string concatenation.
  • Investigate download performance.
  • Avoid multiple document.write() method calls.

Look for these culprits especially inside loops, where delays become magnified.

Discussion

One of the most inefficient functions in the JavaScript language is eval(). This function converts a string representation of an object to a genuine object reference. It becomes a common crutch when you find yourself with a string of an object's name or ID, and you need to build a reference to the actual object. For example, if you have a sequence of mouse rollover images comprising a menu, and their names are menuImg1, menuImg2, and so on, you might be tempted to create a function that restores all images to their normal image with the following construction:

  for (var i = 0; i < 6; i++) {
     
var imgObj = eval("document.menuImg" + i);
     
imgObj.src = "images/menuImg" + i + "_normal.jpg";
  }

The temptation is there because you are also using string concatenation to assemble the URL of the associated image file. Unfortunately, the eval() function in this loop is very wasteful.

When it comes to referencing element objects, there is almost always a way to get from a string reference to the actual object reference without using the eval() function. In the case of images, the document.images collection (array) provides the avenue. Here is the revised, more streamlined loop:

  for (var i = 0; i < 6; i++) {
     
var imgObj = document.images["menuImg" + i];
     
imgObj.src = "images/menuImg" + i + "_normal.jpg";
  }

If an element object has a name or ID, you can reach it through some collection that contains that element. The W3C DOM syntax for
document.getElementById() is a natural choice when working in browsers that support the syntax and you have the element's ID as a string. But even for older code that supports names of things like images and form controls, there are collections to use, such as document.images and the elements collection of a form object (document.myForm.elements["elementName"]). For custom objects, see the later discussion about simulated hash tables. Hunt down every eval() function in your code and find a suitable, speedier replacement.

Another performance grabber is the with construction. The purpose of this control statement is to help narrow the scope of statements within a block. For example, if you have a series of statements that work primarily with a single object's properties and/or methods, you can limit the scope of the block so that the statements assume properties and methods belong to that object. In the following script fragment, the statements inside the block invoke the sort() method of an array and read the arrays length property:

  with myArray {
     
sort();
     
var howMany = length;
  }

Yes, it may look efficient, but the interpreter goes to extra lengths to fill in the object references before evaluating the nested expressions. Don't use this construction.

It takes processing cycles to evaluate any expression or reference. The more "dots" in a reference, the longer it takes to evaluate the reference. Therefore, you want to avoid repeating a lengthy object reference or expression if it isn't necessary, especially inside a loop. Here is a fragment that may look familiar to you from your own coding experience:

  function myFunction(elemID) {
      for (i = 0; i < document.getElementById(elemID).childNodes.length; i++) {
          if (document.getElementById(elemID).childNodes[i].nodeType = = 1) {
              // process element nodes here
          }
      }
  }

In the course of this function's execution, the expression document.getElementById() evaluates twice as many times as there are child nodes in the element whose ID is passed to the function. At each start of the for loop's execution, the limit expression evaluates the method; then the nested if condition evaluates the same expression each time through the loop. More than likely, additional statements in the loop evaluate that expression to access a child node of the outer element object. This is very wasteful of processing time.

Instead, at the cost of one local variable, you can eliminate all of this repetitive expression evaluation. Evaluate the unchanging part just once, and then use the variable reference as a substitute thereafter:

  function myFunction(elemID) {
     
var elem = document.getElementById(elemID);
     
for (i = 0; i < elem .childNodes.length; i++) {
         
if (elem .childNodes[i].nodeType = = 1) {
              // process element nodes here
          }
      }
  }

If all of the processing inside the loop is with only child nodes of the outer loop, you can further compact the expression evaluations:

  function myFunction(elemID) {
     
var elemNodes = document.getElementById(elemID).childNodes;
     
for (i = 0; i < elemNodes.length; i++) {
          if (elemNodes[i].nodeType = = 1) {
              // process element nodes here
          }
      }
  }

As an added bonus, you have also reduced the source code size. If you find instances of repetitive expressions whose values don't change during the course of the affected script segment, consider them candidates for pre-assignment to a local variable.

Next, eliminate time-consuming iterations through arrays, especially multidimensional arrays or arrays of objects. If you have a large array (say, more than about 100 entries), even the average lookup time may be noticeable. Instead, use the techniques shown in Recipe 3.9 to perform a one-time generation of a simulated hash table of the array. Assemble the hash table while the page loads so that any delay caused by creating the table is blended into the overall page-loading time. Thereafter, lookups into the array will be nearly instantaneous, even if the item found is the last item in the many-hundred member array.

As discussed in depth in Recipe 1.2, string concatenation can be a resource drain. Using arrays as temporary storage of string blocks can streamline execution.

Getting a ton of JavaScript code from server to browser can be a bottleneck on its own. Bear in mind that each external .js file loaded into a page incurs the overhead of an HTTP request (with at most two simultaneous connections possible). Various techniques for condensing .js source files are available, such as utilities that remove whitespace and shorten identifiers (often at the cost of ease of source code management and debugging). Most modern browsers can also accept external JavaScript files compressed with gzip (although IE 6 exhibits problems). As you can see, no single solution is guaranteed to work in every situation.

One other impact on loading time is where in the page you place <script> tags that load external .js files. The user may perceive that the entire page is loading faster if your <script src=""> tags are just above the closing </body> tag because images and text start to appear faster. Interaction on the elements that relies on the scripts will still be delayed until the scripts fully load.

The final tip addresses use of the document.write() method to generate content while the page loads. Treat this method as if it were an inherently slow input/output type of operation. Invoke the method as infrequently as possible. If you are writing a lot of content to the page, accumulate the HTML into one string variable, and blast it to the page with one call to document.write().

See Also

Recipe 3.9 for details on creating a simulated hash table from an array; Recipe 3.13 for a rare case where the eval() function can't be avoided; Recipe 1.2 for details on using an array to speed large string assembly.


DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.

blog comments powered by Disqus
JAVASCRIPT ARTICLES

- Project Nashorn to Make Java, JavaScript Wor...
- JavaScript Virus Attacks Tumblr Blogs
- Google Releases Stable Dart Version, JavaScr...
- Khan Academy Unveils New JavaScript Learning...
- Accessing Nitro? There`s an App for That
- JQuery 2.0 Leaving Older IE Versions Behind
- Fastest JavaScript Engine Might Surprise You
- Microsoft Adjusting Chakra for IE 10
- Brendan Eich: We Don`t Need Google Native Cl...
- An Overview of JavaScript Statements
- An Overview of JavaScript Operators
- Overview of JavaScript Variables
- More of the Top jQuery Social Plugins
- The Top jQuery Social Plugins
- More of the Top jQuery Slider Plugins

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials