Debugging JavaScript


God got His creation perfect on the first cut; the rest of us get to debug our work. I think in my entire life I might have gotten two programs right the first try, both of them very short (one-liners don't count). Most of your time programming will be spent trying to figure out why it didn't work right -- or often not at all. This page is explains some useful ideas for fixing JavaScript. Other programming languages and development environments have their own ways of dealing with bugs.

The grown-up languages like C++ and Java have two important debugging tools not available to JavaScript programmers. One of these is the compiler, which stops with an error message when it encounters something it doesn't understand. The error message often is completely unrelated to the actual error, but at least it tells you (more or less) where the problem is. Most compilers keep going and produce additional error messages for subsequent lines, which are even more bizarre and very voluminous, because the compiler tends to become hopelessly confused after the first error. Experienced programmers just fix the first error and try again.

JavaScript has something like a compiler, which converts the script into runtime code slightly more efficient than the text stored in a web page, and that compiler does stop when it gets confused, but error messages make no sense to the average user because it is usually executing on a client computer far far away from the programmer who wrote the code. On my browser the interpreter just gives up and displays nothing when that happens; in Windows you can tell it to enable script debugging, but then it asks you for a debugging tool that knows how to do it -- all of which cost money. Your only hope in such cases is what I call "lion hunting."

The other debugging tool available to C++ and Java lets those programmers step through their program one line at a time. If your browser is FireFox, then you can download FireBug, which lets you do much of the same stuff for JavaScript. It's "open source" which means it's incredibly closed-mind (very hard to figure out, and totally inadequate documentation, but if you persevere, you can make it work for you). If you already paid for Microsoft Office, it has a similar debugging tool, described here. My free alternative, which works in every language, is called "logging".
 

Lion Hunting

Mathematicians have a joke about how to hunt for a lion in a dense forest: You divide the forest in half. The lion must either be in one half or the other, so the half he's in, you divide in half again, and so on, until the tiny fragment of the forest the lion is in is no bigger than the lion, so  you can see it. That works for finding program bugs, too. If you wrote 32 lines of code, and the interpreter refuses to run at all, cut it in half and see if one half fails. If so, the bug is there in that 16-line half. Cut it in half again, and so on. Half of 16 is eight lines, then four, then two. Finally you know which line is failing. Repair that line. There will be more bugs. Find each one the same way.

Unfortunately, you can't always just slice the program in half at line 16. If you have a block with its opening brace before the divider and its closing brace after the divider, cutting it there just introduces a new bug in each half. So you need to be more clever.

One way to divide a program is to replace the body of a function with a pair of empty braces {}. If the program now tries to run, then the bug must be inside the function. Alternatively, you can leave the functions complete, but remove the code that calls them, but if you left out a brace or parenthesis, your only hope is to cut out code.

An easy way to remove code without losing it is to "comment it out" by adding the double slant // in front of each line. Later, after you identify and fix the error, you can just delete the double slant on each line. If you look in my JavaScript interpreter, you will see numerous lines of code commented out. I put them for debugging, then when it was working, I left them there but commented out.
 

Logging

If the program tries to run, but fails, you need to look at what it's doing up to the point of failure. Usually it's some value different that what you thought should be there, probably a mistake or misspelling in calculating that value. My most frequent programming error is a backwards if, where the if-part does what  the else-part should do, and vice versa. Every programming language has a way to output text to some display -- possibly a text file you can view later -- so the most universal debugging tool is nothing more that text output lines every here and there, the same code we saw in our very first JavaScript program. All we need to do is output on each new line whatever values the previous line computed, or the next line needs, or just "I am here now" lines (with a unique label for each line) which trace the flow through ifs and loops and function calls. Usually a few strategic lines will do wonders, but until you get the feel for what you need to know, or if your hunch is insufficient, add more lines and run it again.

If you have a simple need, you can use window.alert("some text") as a one-line log. Later, when we get into animated games, you can look at the source code of my Kickball example, where I used an alert to track why my KickMe function was not working properly (it's now commented out).
 

Real-Life Example

This actually happened. I wrote the string replacement example program of the previous page and nothing displayed. I moved the story initialization line to the front of the code, followed immediately by a "document.write(story)" line to display it (logging), and still nothing. That was before I knew that JavaScript preprocessed the code. I removed everything except those two lines (lion hunting), and they worked fine. Obviously the interpreter was looking at the subsequent code before starting to run.

But I knew the problem was in the function or the loop where it was called. I changed the loop condition to false (so it wouldn't run at all), then stubbed out the function to {} but still nothing. I commented out everything inside the loop. Still nothing. Since its call was now only a comment, I removed the function entirely, and it ran. Now I knew that the problem was the first line of the function, which looked like this:

function replacestring(new,start,len,old) {}
Looking at this, I remembered "new" is a reserved word (used for creating new objects). I changed its spelling to "newly" and it worked. Slowly I put the rest of the code back in and finally changed the false back to true, and did not find any other bugs. I was lucky. You won't be. Maybe next year you will be lucky, if you work hard this year at learning to program.
 

Reserved Words

The list of reserved words is on the Sun website, but I decided to include theme here, because you need to know what not to name your variables and functions:
 
    abstract
  boolean
  break
  byte
  case
  catch
  char
  class
  const
  continue 
  debugger 
  default
  delete
  do
  double
 else
 enum
 export
 extends
 false
 final
 finally
 float
 for
 function
 goto
 if
 implements 
 import
 in
 instanceof 
 int
 interface
 long
 native
 new
 null
 package
 private
 protected
 public
 return
 short
 static
 super
 switch
 synchronized
 this
 throw
 throws
 transient
 true
 try
 typeof
 var
 void
 volatile
 while
 with

Most of these names have meaning only in Sun's proprietary language Java, which JavaScript was intended to resemble. Maybe they will eventually come to mean the same things in JavaScript, but smart programmers won't use them, because none of the older browsers will know about the new stuff. If you want people to use and enjoy your programs, you need to make it easy for them. Abusing your users does not make happy campers.
 

My JavaScript Interpreter

After fiddling with the script examples in this tutorial for a while, I began to realize that finding bugs by lion hunting when the interpreter is telling you nothing at all, is probably overwhelming. So I wrote my own small JavaScript interpreter (in JavaScript :-) which you can use to find syntax errors and other problems.

Unfortunately, JavaScript is in a permanent vegetative state when it comes to data input, so the only way to get the script in for a program to look at it is to make it part of that program's literal data. This is very clumsy and frought with additional errors that you don't need, so I also wrote a small WinXP program "JStest.exe" to add the necessary quotes. This entire tutorial with all incidental files can be downloaded as LearnJSinfo.zip; be sure to unpack it before trying to run things. The JStest.exe program is not very beautiful; it just puts up a file dialog so you can select an HTML document with your script in it. It extracts the script, adds the necessary quotes and escapes, then copies it to the clipboard, so you can paste the result into a copy of the "Jscript.html" page containing the interpreter.

Initially this page has the whole interpreter, plus a small demo script for test purposes, which starts out like this:

<html> <head> <title>JSinterp 10 Dec 22</title> </head> <body> <script>

var theScript = ""
+ '\r function fact(n) {'
+ '\r   if (n==0) return 1;'
+ '\r   return fact(n-1)*n;}'
+ '\r document.write("3! = "+fact(3));'
+ '\r'
+ '\r var whom = 4;'
+ '\r document.write(" Hello "+(whom+3));'
+ "";

var Logging = 2; // =0 is no log; =1 shows source function calls & returns
  // =2 shows var ass'ts & some comments; =3 interp'd flow control (=4 includes if)
  // =5 shows interpreter flow control (nonterminal entry/exit) & tokens
  // =6 is full interp trace; =7 shows extra info; >7 logs in func defn & GeTok
var LogAtLine = 0; // >0 turns on Logging when it reaches that code line
var LogAfter = 0; // >0 turns on Logging after that many tokens
...

Using a text editor (like WordPad) you replace the nine lines starting with "var theScript = " (shown here in red) with whatever is in the clipboard after you run JStest.exe, then open the page in your browser. It will take a while to interpret your script, but if it finds errors, they will be displayed on the page. If it gets to the end of your script without reporting any problems, then your original script will probably run in a browser -- if not, I need you to send me your script, so I can fix whatever went wrong.

There is more on Using the JavaScript Debug Interpreter, which you might wish to become familiar with.

Next: A Bigger Example

Tom Pittman
Rev 2010 December 23