Functions are important parts of JavaScript programs. They are used for dividing code up into reusable chunks that does one thing mostly.
Therefore in order to have clean JavaScript code, we have to have easy to understand functions.
In this article, we’ll look at more parts of a function including output arguments, command query separation., throwing exceptions, and duplicated code.
Output Arguments
Output arguments are arguments that are accepted by a function that’s directly returned by the function.
This is weird since arguments are usually interpreted as inputs rather than being directly used as outputs. There aren’t many use cases for this. Usually, the arguments are computed in some way by combining them or checking them and returning the results by these checks and operations.
In JavaScript, if we want to change a shared state, we can put shared as state as class members and then we can have methods to manipulate the class members.
So we should write something like:
class FruitStand {
constructor(numFruits) {
this.numFruits = numFruits;
}
addFruit() {
this.numFruits++;
}
removeFruit(numFruits) {
this.numFruits--;
}
}
rather than returning arguments that are passed in.
Command Query Separation
A function should either change the state of an object, or it should return some information about an object. However, it shouldn’t do both.
For example, we shouldn’t have a function like the following:
const setProperty = (obj, property, value) => {
obj[property] = value;
return value;
}
The function changes the obj
object in place and also returns the value.
It does 2 things, which isn’t good, and the name doesn’t convey that also returns some information about the object. This misleads the user of this function when he or she didn’t read the function definition and just goes by the name.
Therefore, it’s better to separate the property setting and returning the value as follows:
const setProperty = (obj, property, value) => {
obj[property] = value;
}
const getProperty = (obj, property) => {
return obj[property];
}
It’s just much better to have functions that do one thing each like we have above so people don’t get confused about what they’re doing.
Throwing Exceptions in Better than Returning Error Codes
Returning error codes violate the command and query separation rule that we mentioned above. This is because the function that returns something returns it when an error occurs and returns something else when the function runs successfully.
This means the function both do something, which forms the command part and returns something, which forms the query part.
It should only do one or the other. Since the main purpose of the function is to do something rather than return something, so it should just do the command part rather than also returning an error code.
This means instead of writing like the following:
const setProperty = (obj, property, value) => {
obj[property] = value;
if (!value) {
return 'Value not found';
}
}
We should instead throw an exception as follows:
const setProperty = (obj, property, value) => {
if (!value) {
throw new Error('Value not found');
}
obj[property] = value;
}
Then we can catch and handle it if we need to:
try {
let obj = {};
setProperty(obj, 'foo', null)
} catch (ex) {
console.log(ex);
}
We can eliminate lots of error code checking conditional statements with try...catch
instead of having an if statement to check each error code returned by a function.
Extract Try…Catch Blocks
try
blocks shouldn’t contain normal processing code. This is because it makes it confusing as to where we’re expecting errors to be thrown.
Instead, we should just wrap the code that we expect to have errors thrown inside a try
block. Then we can write a catch
block right below it to catch the exception.
For example, if we have normal code and code where exceptions need to be caught, then we can write the code as follows:
const functionThatThrowsError = () => { //... };
const doSomething = () => { //... };
const runFunctionThatThrowsError = () => {
try {
functionThatThrowsError();
} catch (ex) {
console.log(ex);
}
}
const runEverything = () => {
doSomething();
runFunctionThatThrowsError();
}
The code above isolates the exception throwing and handling code into its own function, which makes it clear to the reader that the particular throws an exception that needs to be handled.
Don’s Repeat Yourself
Repeating code is definitely a big no-no. Repeated code has to be changed in multiple places when one thing changes. It’s also easy to miss repeated code.
The code also becomes a lot more bloated since they’re repeated in different places.
There’re many ways to eliminate duplicated code in JavaScript, like with functions and modules. We should use those as much as possible.
If we have repeated object declarations, then we should also use classes as a template to create those objects.
Repeated literals should be assigned to a constant and reused.
Conclusion
Output arguments should be eliminated because we don’t have to use them to change shared states in JavaScript.
Functions that do something should be separated from functions that return something.
Also, pieces of code that throw exceptions are preferred to code that returns error codes. The exception throwing code should be separated into its own function when we need to handle the exception to make our intentions clear that we want to handle the exceptions.
Repeating code is bad. It takes more time to change the code since we have to change them in multiple places. We should take steps to eliminate them by using code that can be accessed in different places.
The post JavaScript Clean Code — Function Exceptions and Duplicate Code appeared first on The Web Dev.
Top comments (0)