Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ajax In Action (2006).pdf
Скачиваний:
63
Добавлен:
17.08.2013
Размер:
8.36 Mб
Скачать

Debuggers 571

Figure A.4 The Komodo IDE provides high-quality outlining features for JavaScript objects and can understand a number of coding idioms. Here the code outliner has recognized that various functions belong to the ObjectViewer prototype.

ticated outliner that recognizes JavaScript classes as well as functions and methods (figure A.4). As a general-purpose scripting IDE, it deals only with JavaScript the generic language, not the browser-based implementations. As such, it is most useful when developing domain models for Ajax. Komodo is a commercial tool with free trials available. As an interesting aside, the Komodo UI is built using the XML-based XUL toolkit used to create the Firefox web browser.

In the next section, we’ll consider another key tool in the developer’s arsenal: the source code debugger.

A.3 Debuggers

The behavior of simple computer programs can often be figured out by looking at the code, but larger, more complex programs are often too large to hold in your mind all at once. Debugger tools provide a way of controlling the flow of execution of a piece of running code, allowing it to be stopped and started manually and the state of the program inspected while it is running.

572APPENDIX A

The Ajax craftsperson’s toolkit

A.3.1 Why we use a debugger

Debuggers provide a very practical way of finding out what a program does. In any programming effort, a debugger can be useful in testing whether you have understood a piece of code correctly. In Ajax, this is particularly valuable.

When the term debugger is used, most developers tend to think of source code debuggers, and server-side and JavaScript debuggers are indeed handy to have at your disposal when writing Ajax. However, it is also helpful to be able to debug network traffic when writing Ajax, as HTTP can be surprisingly complicated, too. In the following sections, we’ll consider both source code and HTTP debugging tools. Let’s look at the state of JavaScript debuggers first.

A.3.2 JavaScript debuggers

Being able to debug JavaScript code is especially useful because of the fluidity of the language. A C# or Java programmer generally knows which properties and methods are available on a given object by examining its class definition and knows the types and number of a method’s arguments from its declared signature. It isn’t always possible with JavaScript, though, to work out from the code how many arguments a function will be invoked with, or even what the variable this will resolve to inside a function. This latter issue is particularly problematic for callback handlers, for which the invocation of the function may be done by an unknown object or by the browser itself.

At its simplest, a source code debugger allows the user to set breakpoints that halt program execution and hand it over to the user when that line of code is executed. The user may then step through the code a line at a time, inspecting the values of any variables that are in scope, or resume normal execution until the next breakpoint is encountered. In JavaScript, breakpoints may be set by the debugger tool itself or by the coder, by adding a debugger statement to the code. For example, when the browser executes the following code

var x=3; var y=x*7; debugger; var z=x+y;

control will be handed over to any debugger that is registered with the browser on the third line of code (figure A.5), at which point the values of variables x and y can be inspected. z has not been declared yet and so can be inspected only after the user has stepped the debugger forward over the fourth line.

Debuggers 573

Figure A.5

Using the JavaScript debugger statement triggers a breakpoint programmatically.

This defines the basic functionality of a source-debugging tool. A more sophisticated debugger may support further features, as discussed next.

Call stack navigation

When a function is executed in JavaScript, a new execution context is created, with its own set of local variables. When a debugger stops inside a function, it can see the local variables inside that function but not those in the function that called it. Consider the following example:

function doASum(){ var a=3;

var b=4;

var c=multiply(a-2,b+6); return (a+b)/c;

}

function multiply(var1,var2){ var n1=parseFloat(var1); var n2=parseFloat(var2); debugger;

return n1*n2;

}

At the point at which the debugger is invoked, we can see variables n1, n2, var1, and var2. While examining a problem with our program, we may decide that the issue lies with the arguments being passed into our function. We need to know what values a and b held in the enclosing doASum() method. We could set an extra breakpoint in doASum() and run the program again, but it might take us some time to return to this state in a complex program. If the debugger supports call stack navigation, then we can simply move up the call stack to the doASum() function and inspect its state as though we had set a breakpoint on the third line, where multiply() is invoked (figure A.6). In a complex program, the call stack may be very deep, and the debugger is capable of moving up and down among all layers.

574APPENDIX A

The Ajax craftsperson’s toolkit

Figure A.6 The Mozilla Venkman debugger allows inspection of local variables in functions higher up the call stack than the current point of execution.

Watching expressions

Some debugger tools are capable of evaluating expressions on the fly and allow the user to predefine code expressions that will be reevaluated as the debugger moves across the code. These expressions can make use of any variables currently in scope, allowing the developer to interact with the program while it is running.

Conditional breakpoints

Setting a breakpoint at a particular point in the code can give a developer finegrained control over when to invoke the debugger, but in some cases, this control is not enough. When executing a loop, for example, any breakpoint inside the body of the loop will be executed each time the loop is executed. Let’s take the following example:

Debuggers 575

for (var i=0;i<100;i++){ var divisor=i-57;

var val=42/divisor; plotOnGraph(i,val);

}

Running this code causes an exception to be thrown midway through the loop. In this case, a quick inspection of the code tells us that this will happen when i is 57, and we attempt to divide by zero. However, let’s pretend for now that it isn’t so obvious, as will often be the case with real-world code. We suspect that the divisor being set to zero is the problem but don’t know when such a condition will occur.

We could set a debugger breakpoint inside the loop:

for (var i=0;i<100;i++){ var divisor=i-57; debugger;

var val=42/divisor; plotOnGraph(i,val);

}

but we would need to click the resume button on our debugger numerous times to step through the loop to the point where we encounter the error condition. If we’re being clever, we can test for the error condition in our code:

for (var i=0;i<100;i++){ var divisor=i-57;

if (divisor==0){ debugger; } var val=42/divisor; plotOnGraph(i,val);

}

This will take us straight to the fifty-seventh iteration of the loop, the one at which our error condition occurs. We could describe this as a conditional breakpoint— that is, it will break the flow of execution only if a certain condition is met.

We can set up conditions in this way only by modifying the code. However, if we are assigning breakpoints through the debugger IDE, we can set a condition on the breakpoint independently of the actual code (figure A.7). Some debuggers do support this facility, allowing the user to attach expressions to a breakpoint, and break the flow only if the expression evaluates to true.

Changing the values of variables

If we encounter an error condition, our program execution halts. In a debugging session, we may realize what the solution is but want to coerce the program into continuing anyway, in order to test some later piece of code under the current set of conditions.

576APPENDIX A

The Ajax craftsperson’s toolkit

Figure A.7 Setting up a conditional breakpoint in the Mozilla Venkman debugger

Some debuggers will allow us to do this by providing write as well as read access to local variables (figure A.8). In the case of our loop example shown previously, if we know that a divisor value of 0 is going to be problematic but want to explore some code following the loop, we could temporarily reassign a value of 1 to the divisor, letting the flow of execution continue.

Figure A.8 Changing the value of a variable in a running program using the Mozilla Venkman debugger