Following the previous post about principles of debugging web applications, it is time to explore JavaScript debugging in practice.

Browser Developer Tools

My personal favorite is the Chrome Developer Tools. Safari and Firefox don’t match the high standards set by Chrome, but they are getting better. Firefox has a combination of Firebug and the improved Firefox Developer Tools. If the Firefox team continues doing an excellent job at improving their built-in Developer Tools, Firebug may become obsolete one day.

Regardless of your personal preference, you should be able to test and debug any code in any browser that you target. This may or may not include the famous Internet Explorer 8.

Familiarize yourself with the developer tools of your choice. You may get additional debugging support from your IDE (Integrated Development Environment), or third party software.

Knowledge of debugging fundamentals is transferable from one tool to another. In fact, I learned the basics of debugging in the 90s using Borland’s C developer environment. Breakpoints, conditional breakpoints and watches were exactly the same as in your latest Chrome Developer Tools. I caught my first exception in Java near the year 2000. The concept of stack traces still apply, and even though JavaScript terminology calls these objects Errors, checking the stack trace is similarly useful as before.

Some knowledge is specific to front-end development. Examples:

DOM inspection,

DOM breakpoints,

debugging events,

profiling memory leaks.

Breakpoints

It is possible to add a breakpoint in the source code using the debugger statement. Once a debugger statement is reached, execution stops. The context of the current scope is exposed in the console, together with all local and global variables. Values of variables can be inspected by moving the mouse cursor over them.

Conditional breakpoints can also be created in the code:

if ( condition ) { debugger; } 1 2 3 4 5 if ( condition ) { debugger ; }

Breakpoints and conditional breakpoints can also be inserted in the Developer Tools of your choice. In the Chrome Dev Tools, clicking on a line number in the Sources view adds a breakpoint. If you right click on a breakpoint and select “Edit Breakpoint”, you can also add a condition.

Break on node change

In case your task is to debug junk code, you may end up asking why a DOM node was modified during execution. The Chrome Developer Tools gives you a handy breakpoint to detect node changes in the element tree.

In the Elements view, right click on an element and select the “Break on…” item from the context menu.

Possible types of DOM breakpoints:

Nodes change in the sub-tree of the selected node,

Attributes of the selected node are modified,

The node is removed.

Avoid logging reference types

When logging an object or an array, the values of primitive types inside the logged reference may change. When viewing a reference type, bear in mind that code executed between the time of logging and observation may influence the observed values.

For instance, execute the following code in your Chrome Developer Tools.

var wallets = [{ amount: 0 }]; setInterval( function() { console.log( wallets, wallets[0], wallets[0].amount ); wallets[0].amount += 100; }, 1000 ); 1 2 3 4 5 6 7 8 var wallets = [ { amount : 0 } ] ; setInterval ( function ( ) { console . log ( wallets , wallets [ 0 ] , wallets [ 0 ] . amount ) ; wallets [ 0 ] . amount += 100 ; } , 1000 ) ;

While the second and the third logged attribute reflect the correct values, the Object refrence in the first attribute shows unreliable values. The value of the amount field is assigned at the time of revealing the attribute in the Developer Tools. This value stays the same regardless of how many times you close and reopen the same reference.

Always know what you are logging. Either log primitive types, use watch expressions with breakpoints. In case of asynchronous code, avoid logging reference types.

Tabular logs

In some developer tools, you can use console.table for logging arrays of objects on the console.

Try executing the following row in your Chrome Developer Tools:

console.table( [ { id: 1, name: 'John', address: 'Bay street 1' }, { id: 2, name: 'Jack', address: 'Valley road 2.' }, { id: 3, name: 'Jim', address: 'Hill street 3.' } ] ); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 console . table ( [ { id : 1 , name : 'John' , address : 'Bay street 1' } , { id : 2 , name : 'Jack' , address : 'Valley road 2.' } , { id : 3 , name : 'Jim' , address : 'Hill street 3.' } ] ) ;

The result is a nice looking table. All primitive types are displayed right away. Their values reflect the state at the time of logging. Complex types are logged displaying their types. Their contents is not possible to browse. Therefore, console.table should only display two dimensional data structures of form arrays of objects having primitive values.

XHR Breakpoints

Sometimes you may encounter an erroneous AJAX request. If you are not able to identify the code submitting the request right away, XHR breakpoints save you time. XHR breakpoints stop execution of the code when a special type of AJAX request is submitted and direct the user to the code segment submitting the request.

In the Sources tab of the Chrome Developer Tools, one of the defined breakpoint types is XHR Breakpoints. When clicking on the + icon, you can enter URL fragments. The JavaScript code breaks whenever such a URL fragment appears in the URL of an AJAX request.

Event Listener Breakpoints

All types of events may be caught by the Chrome Developer Tools, making it possible to debug what happens if the user presses a key, clicks a mouse button etc.

Pause on Exceptions

The Chrome Developer Tools allows you to pause execution of your JavaScript code upon throwing an exception. This makes it possible for you to observe the state of your application at the time when the Error object was created.

Snippets

The left panel of the Sources Tab has a Snippets sub-tab for the purpose of storing code snippets that help you debug your code.

If you keep on using the console for debugging and you write the same code over and over again, it is high time abstracting your code into a debugger snippet. This way, you can even transfer your debugging magic to your peers.

Paul Irish published some basic debugging snippets such as inserting a breakpoint before the execution of a function. It is worth examining these snippets and searching for others on the web.

Inserting a breakpoint before a function

If you have access to the reference of a function, you can also insert a breakpoint that stops execution just before the function is called. If f is the function you would like to debug, the debug( f ) statement adds this breakpoint.

Unminify minified code

Use source maps whenever possible. In some cases, source maps don’t make it to production, however, you should not debug on production anyway.

In case source maps are not available, your last resort is the Pretty Print button in the Sources tab of your Chrome Developer Tools. The {} Pretty Print button is below the textarea displaying the source code. Pretty Print beautifies the source code and changes the line numbers for easier debugging and more useful stack traces.

Pretty Print should still be used as a last resort. Uglified code is still ugly in a sense that the names are still not semantic.

Console bookmarks for DOM elements

Both Chrome Developer Tools and Firebug gives you bookmarks for the DOM elements you last clicked on in the Elements (Chrome) or HTML (Firebug) tab. If you selected elements A , B and C in this order,

$0 gives you C ,

gives you , $1 gives you B ,

gives you , $2 gives you A .

If you select another element D , then $0 , $1 , $2 and $3 will hold the DOM elements D , C , B and A respectively.

Accessing the call stack

The command console.trace logs a stack trace including line numbers.

var f = function() { g(); } var g = function() { h(); } var h = function() { console.trace('trace in h'); } f(); 1 2 3 4 5 6 7 var f = function ( ) { g ( ) ; } var g = function ( ) { h ( ) ; } var h = function ( ) { console . trace ( 'trace in h' ) ; } f ( ) ;

The Sources tab in Chrome Dev Tools also displays the Call Stack just below the Watch expressions.

Audits

Performance audit tools often come handy. These tools act as a generalized leak buster. They detect where your website may be optimized. As these tools don’t have any understanding of your product, some of the advice may be ignored. More often than not, the audit tool points out valid areas in which your website can be optimized significantly.

Example audit tools:

Audit tab of the Chrome Developer Tools

YSlow

Practice makes perfect

While some debugging techniques may be familiar to you, others may save you some time. If you start applying some of these techniques in practice, I suggest re-reading this article in a couple of weeks. You will be surprised how much your focus may change in a matter of weeks.