Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62
Subscribe to my email list now at http://jauyeung.net/subscribe/
JavaScript is a very forgiving language. It’s easy to write code that runs even if it has issues in it.
In this article, we’ll look at how to program defensively so that errors are handled gracefully.
Protecting Our Program from Invalid Inputs
We got to check our program for invalid inputs so that they won’t propagate in our systems.
For instance, we got to check for things like null
, undefined
, or invalid values all overt our code.
Also, we got to be aware of common security issues like SQL injection or cross-site scripting.
To do that, we do the following things in our program.
Check the Values of All Data from External Sources
We just can’t trust users to always enter valid data and that attackers won’t use exploits in our code.
Therefore, we should check everything thoroughly so that we can make sure that no invalid data gets propagated anywhere.
For instance, we should check for number ranges if code need numbers in a certain range.
Also, we shouldn’t use things like the eval
function or the Function
constructor which runs code from a string.
Check the Values of All Function Input Parameters
We got to check the values before we use them in a function.
For instance, we can write a function that dispenses beer to adults only as follows:
const dispenseBeer = (age) => {
if (age < 21) {
return 'you are too young';
}
//...
}
We check age
before dispensing beer so that we won’t mistakenly dispense beer to underage people.
Decide How to Handle Bad Inputs
Bad inputs have to be handled in a graceful way.
We got to check them and then find a good way to deal with them.
Returning a message or throwing exceptions are good ways to handle invalid inputs.
For instance, our dispenseBeer
function can be changed to show an alert box instead:
const dispenseBeer = (age) => {
if (age < 21) {
alert('you are too young');
return;
}
//...
}
Or we can write the following:
const dispenseBeer = (age) => {
if (age < 21) {
throw new Error('you are too young');
}
//...
}
We don’t need the return since throwing exceptions would end the execution of the function.
Assertions
Also, we can use assertions to check for inputs before we run the function.
For instance, we can write the following using the assert
module as follows:
const assert = require('assert');
const dispenseBeer = (age) => {
try {
assert.equal(age >= 21, true);
//...
}
catch {
return 'you are too young';
}
}
This does the same thing as throwing an error since it checks if age
is bigger than 21.
Otherwise, an assertion error would be thrown and ‘you are too young’
is returned.
This is the same as throwing an exception.
There’re many other kinds of assertions we can make with the assert
module.
They include:
- checking for equality
- checking for inequality
- checking for exceptions thrown
- checking for deep equality
- checking for deep inequality
- …and others
Guidelines for Using Assertions
There’re some guidelines for using assertions.
Use Error-Handling Code for Conditions that are Expect to Occur
Error handling code should be used for checking for conditions that might be invalid.
In this case, we should handle those cases instead of using assertions.
Things like bad input values should be checked with error handling code.
Use Assertions for Conditions that Should Never Occur
If something should never occur, then we should use assertions to stop the code from running if they’re encountered.
We can run the rest of the code assuming that condition that we asserted to never occur won’t occur.
Avoid Putting Executable Code into Assertions
We should only use assertions to only check for things.
It keeps them clean and prevents any confusion.
Use Assertions to Document and Verify Preconditions and Postconditions
Preconditions that should be verified should have an assertion.
Since it;’s in the code, then we know what the codes check for when we read the code before running the rest of the code.
When the program ends, we can check for postconditions with assertions to check for conditions we look for.
Conclusion
We should check for invalid conditions before we run anything.
To do that, we can use assertions or error handling code. Assertions are better for checking for conditions that should never occur while error handling code are better for other cases.