-
Debugging is the process of finding exactly what isn't working and fixing it
-
These issues generally come in three forms:
- syntax errors that prevent a program from running
- runtime errors when code fails to execute or has unexpected behavior
- semantic (or logical) errors when code doesn't do what it's meant to.
-
Example of a syntax error - often detected by the code editor:
funtcion willNotWork(
console.log("Yuck");
}
// "function" keyword is misspelled and there's a missing parenthesis
- Here's an example of a runtime error - often detected while the program executes:
function loopy() {
while(true) {
console.log("Hello, world!");
}
}
// Calling loopy starts an infinite loop, which may crash your browser
- Example of a semantic error - often detected after testing code output:
function calcAreaOfRect(w, h) {
return w + h; // This should be w * h
}
let myRectArea = calcAreaOfRect(2, 3);
// Correct syntax and the program executes, but this gives the wrong answer
- The
console.log()
method, which "prints" the output of what's within its parentheses to the console, will likely be the most helpful debugging tool.
console.clear()
to clear the browser console
- You can use
typeof
to check the data structure, or type, of a variable. This is useful in debugging when working with multiple data types. - Type errors can lurk in calculations or function calls. Be careful especially when you're accessing and working with external data in the form of a JavaScript Object Notation (JSON) object.
- JavaScript recognizes six primitive (immutable) data types:
Boolean
,Null
,Undefined
,Number
,String
, andSymbol
(new with ES6) and one type for mutable items:Object
. Note that in JavaScript, arrays are technically a type of object.
- One syntax-level issue that fast typers can commiserate with is the humble spelling error.
- Transposed, missing, or mis-capitalized characters in a variable or function name will have the browser looking for an object that doesn't exist - and complain in the form of a reference error.
- JavaScript variable and function names are case-sensitive.
- Another syntax error to be aware of is that all opening parentheses, brackets, curly braces, and quotes have a closing pair.
- Forgetting a piece tends to happen when you're editing existing code and inserting items with one of the pair types.
- Also, take care when nesting code blocks into others, such as adding a callback function as an argument to a method.
- One way to avoid this mistake is as soon as the opening character is typed, immediately include the closing match, then move the cursor back between them and continue coding. Fortunately, most modern code editors generate the second half of the pair automatically.
- Example:
let myArray = [1, 2, 3];
let arraySum = myArray.reduce((previous, current) => previous + current);
console.log(`Sum of array values is: ${arraySum}`);
- JavaScript allows the use of both single (
'
) and double ("
) quotes to declare a string. Deciding which one to use generally comes down to personal preference, with some exceptions. - Having two choices is great when a string has contractions or another piece of text that's in quotes. Just be careful that you don't close the string too early, which causes a syntax error.
- Example:
// These are correct:
const grouchoContraction = "I've had a perfectly wonderful evening, but this wasn't it.";
const quoteInString = "Groucho Marx once said 'Quote me as saying I was mis-quoted.'";
// This is incorrect:
const uhOhGroucho = 'I've had a perfectly wonderful evening, but this wasn't it.';
- You can escape the quotes inside the string by using the backslash () escape character:
// Correct use of same quotes:
const allSameQuotes = 'I\'ve had a perfectly wonderful evening, but this wasn\'t it.';
- The assignment operator (
=
) in JavaScript assigns a value to a variable name. - And the
==
and===
operators check for equality (the triple===
tests for strict equality, meaning both value and type are the same).
- When a function or method doesn't take any arguments, you may forget to include the (empty) opening and closing parentheses when calling it.
- Example:
function myFunction() {
return "You rock!";
}
let varOne = myFunction; // set to equal a function
let varTwo = myFunction(); // set to equal the string "You rock!"
- The next bug to watch out for is when a function's arguments are supplied in the incorrect order.
- If the arguments are different types, such as a function expecting an array and an integer, this will likely throw a runtime error.
- If the arguments are the same type (all integers, for example), then the logic of the code won't make sense.
- Make sure to supply all required arguments, in the proper order to avoid these issues.
- Off by one errors (sometimes called OBOE) crop up when you're trying to target a specific index of a string or array (to slice or access a segment), or when looping over the indices of them.
- JavaScript indexing starts at zero, not one, which means the last index is always one less than the length of the item.
- If you try to access an index equal to the length, the program may throw an "index out of range" reference error or print
undefined
. - When you use string or array methods that take index ranges as arguments, it helps to read the documentation and understand if they are inclusive (the item at the given index is part of what's returned) or not.
- Here are some examples of off by one errors:
let alphabet = "abcdefghijklmnopqrstuvwxyz";
let len = alphabet.length;
for (let i = 0; i <= len; i++) {
// loops one too many times at the end
console.log(alphabet[i]);
}
for (let j = 1; j < len; j++) {
// loops one too few times and misses the first character at index 0
console.log(alphabet[j]);
}
for (let k = 0; k < len; k++) {
// Goldilocks approves - this is just right
console.log(alphabet[k]);
}
- Sometimes it's necessary to save information, increment counters, or re-set variables within a loop.
- A potential issue is when variables either should be reinitialized, and aren't, or vice versa.
- Printing variable values with each cycle of your loop by using console.log() can uncover buggy behavior related to resetting, or failing to reset a variable.
- Example :
function zeroArray(m, n) {
// Creates a 2-D array with m rows and n columns of zeroes
let newArray = [];
for (let i = 0; i < m; i++) {
// Adds the m-th row into newArray
let row = [];
for (let j = 0; j < n; j++) {
// Pushes n zeroes into the current row to create the columns
row.push(0);
}
// Pushes the current row, which now has n zeroes in it, to the array
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
- Loops are great tools when you need your program to run a code block a certain number of times or until a condition is met, but they need a terminal condition that ends the looping.
- Infinite loops are likely to freeze or crash the browser, and cause general program execution mayhem, which no one wants.
- It's the programmer's job to ensure that the terminal condition, which tells the program when to break out of the loop code, is eventually reached.
- One error is incrementing or decrementing a counter variable in the wrong direction from the terminal condition.
- Another one is accidentally resetting a counter or index variable within the loop code, instead of incrementing or decrementing it.