Module 6 : Errors, exceptions, debugging, and troubleshooting
After completing Module 6, the student will:
gain an understanding of the differences between syntactic, semantic, and logical errors;
understand the concept of an exception and distinguish between the basic exceptions generated by JS when an error occurs: SyntaxError, ReferenceError, TypeError, RangeError;
have the ability to handle exceptions using the try-catch-final statement;
be able to generate their own exceptions using the throw statement;
have the skills to use the debugger for basic analysis of their own code, including: step-by-step execution, viewing and modifying variables, measuring code execution time.
There are syntax errors, semantic errors (reference errors), and logical errors.
Errors without exceptions?
In JavaScript, not all erroneous situations throw exceptions. Many of them are handled in a slightly different way. The best example is arithmetic errors.
console.log(100 / 0); // -> Infinity
console.log(100 * "2"); // -> 200
console.log(100 * "abc"); // -> NaN
console.log(Math.pow("abc", "def")); // -> NaN
An attempt to perform an arithmetic operation on a string that does not represent a number (i.e. that cannot be converted) will result in NaN
(not a number). At least two of these cases are clearly wrong (the first and the third), but instead of exceptions, the information about the error is the specific value that is returned.
The conclusion is quite simple – if you are learning about a new function or operator, you have to check in the documentation (e.g. on the MDN page) how they behave in the case of errors. Some of them will generate exceptions, while others will return some specific values. Depending on that, you will be able to properly prepare yourself for handling errors using the try method or simple conditional instructions. By the way, for the examples just shown, the most sensible solution would be to check if the provided values really are numbers (remember the typeof
operator?)
we cannot assume that the user will provide data in the format we require
it is our responsibility to anticipate potentially dangerous situations.
let sX = prompt("Enter the first number");
let sY = prompt("Enter the second number");
let x = Number(sX);
let y = Number(sY);
if (Number.isFinite(x) && Number.isFinite(y) && y !== 0) {
console.log(x / y);
} else {
console.log("incorrect arguments");
}
We use the Number.isFinite
method for this purpose. It returns true
if the argument is a correct number, and false
if it is, for example Infinity
or NaN
.
Basic types of errors
1. SyntaxError
As we previously said, a SyntaxError
appears when a code is ill-formed, when there are typos in the keywords, unmatching parentheses or brackets, etc. The code can’t even be executed, as JavaScript isn’t able to understand it.
"use strict";
iff (true) { //-> Uncaught SyntaxError: Unexpected token '{'
console.log("true");
}
In the example above, we’ve made a typo in the keyword if
, adding an additional letter f. The JavaScript engine treats the unknown name as a function call (it finds the round brackets after iff) and is surprised by the presence of a curly bracket.
2. ReferenceError
It occurs when we try to access a function or a variable that doesn't exist.
let a = b; // -> Uncaught ReferenceError: b is not defined
fun(); / -> Uncaught ReferenceError: fun is not defined
3. TypeError
you try to perform an operation on it that is not acceptable.
const someConstValue = 5;
someConstValue = 7; // -> Uncaught TypeError: Assignment to constant variable.
let someNumber = 10;
someNumber.length(); // -> Uncaught TypeError: someNumber.length is not a function
The throw statement and custom errors
To throw an exception, we use the throw instruction. It is followed by any value that will be treated as an exception. It can be, for example, a number, or one of the ready-made error objects (e.g. RangeError
).
console.log("start"); // -> start
try {
throw 100;
} catch (error) {
console.log(error); // -> 100
}
console.log("end"); // -> end
Function factorial(n) {
if (n > 20) {
throw new RangeError("Max value 20");
}
let result = 1;
for (; n > 1; n--) {
result = result * n;
}
return result;
}
console.log(factorial(20)); // -> 2432902008176640000
console.log(factorial(1000)); // -> Uncaught RangeError: Max value 20
Debugging - Hands on
Add debugger;
in-between JavaScript code.
Open developer tools. Click on the number 15. This is how we set the breakpoint (the line will be highlighted). Click on the line number again if you want to remove the breakpoint (do not delete it yet).
console.log("Before debugger");
debugger;
console.log("After debugger");
To be honest, when debugging code, we rarely use the debugger statement. Most often, at the place where the program should stop, we just indicate it using breakpoints set directly in the Developer Tools.
Call stack
If you click on the name of the outer function on the Call Stack, you will be taken to the context of that function (note that the selection of the current line has changed). Try calling the same command again in the console:
console.log(name); // -> outer
Watch (or Watch expressions)
we can enter the name of the variable whose value changes we want to track. It allows us to view and modify the variables without using the console.