JavaScript Clean Code — Function Parameters and Side Effects
John Au-Yeung
Posted on July 21, 2020
Functions are an important part of JavaScript programs. They’re used for dividing code up into reusable chunks. Therefore in order to have clean JavaScript code, we need to have easy to understand functions.
In this article, we’ll look at more properties of good functions, including flag arguments, dyadic and triadic functions, and side effects.
Flag Arguments
Boolean parameters should be used sparingly. It makes the function signature more complex and it tells us that the function does more than one thing (has multiple paths).
Dyadic Functions
Dyadic functions are harder to understand than functions that take fewer arguments. However, sometimes they make sense. For example, if we have an object that holds the Cartesian coordinate, then it should take 2 arguments.
For example, we can have a class with a constructor that takes 2 arguments as follows:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
const point = new Point(1, 2);
It’s pretty much impossible to define it any other way.
However, we do have to be aware that it takes more time and brainpower than functions that take fewer arguments.
Triadic Function
Functions with 3 arguments take a lot of time and brainpower to understand that functions that take 2 arguments.
There’re many more combinations of arguments to think about that if there’re 2 or fewer arguments.
Combining Arguments into Objects
If a function takes many arguments, we should consider combining them into objects.
This is especially true if they’re related. For example, the following function takes many parameters:
const describeFruit = (color, name, size, price, numSeeds, type) => {
return `${fruitName} is ${fruitColor}. It's ${fruitSize}. It costs ${price}. It has ${numSeeds}. The type if ${type}`;
}
6 parameters is probably too many. We can clean this up by passing in an object instead:
const describeFruit = (fruit) => {
return `${fruit.name} is ${fruit.color}. It's ${fruit.size}. It costs ${fruit.price}. It has ${fruit.numSeeds}. The type if ${fruit.type}`;
}
As we can see, it’s much cleaner. We don’t have to worry about passing in many arguments.
It also fits better on the screen since it’s shorter.
5 parameters are probably the maximum that should be in a function.
Verbs and Keywords
It’s a good idea to include verbs and keywords into our function names since they do something, which means an action word in the name is justified.
Also, we need to know what things we’re applying the action to. This means that we have to add some keywords to do that.
For example, a good function definition that meets this rule would be something like:
const copyArray = (array) => [...array];
The copyArray
name let us know that our function makes a copy of an array.
It also lets us know what we’re passing into the function, and that’s clearly an array.
Photo by Autri Taheri on Unsplash
No Side Effects
Side effects are code in a function that makes changes to things that are outside the function.
This isn’t good because it’s making hidden changes to things that are outside the function.
We should avoid this as much as possible since it does something unexpected and also it takes testing harder because, in addition to taking in arguments, doing things and returning a result, it also makes changes to something outside the function that we have to account for.
This means that we have to test things outside of what the function returns.
For example, if we have:
let numFruits = 1;
const addFruit = () => {
numFruits++;
}
const removeFruit = () => {
numFruits--;
}
Then we have 2 functions with side effects because they’re both changing the numFruits
variable which is outside each function.
A better way to write these functions is to write them as pure functions. Pure functions are functions that returns the same thing given the same arguments are passed in. Also, it has no side effects.
Pure functions are easier to test because of that and the behavior of them is also predictable.
We can rewrite the code above by writing them as follows:
let numFruits = 1;
const addFruit = (numberOfFruits) => numberOfFruits + 1;
const removeFruit = (numberOfFruits) => numberOfFruits - 1;
numFruits = addFruit(numFruits);
numFruits = removeFruit(numFruits);
We now have 2 functions that take in one numFruits
parameter and return a number that’s one bigger or smaller respectively.
Then we can use them to change the numFruits
variable that we have outside the functions.
As we can see, they do nothing to the numFruits
but rather it returns the numberOfFruits
parameter plus 1 or minus 1 respectively.
If we write tests for them, then we can test them easily by passing in the input and checking if the output is what we want. This is much better than committing side effects to a variable that might be available to the test code.
Conclusion
Flag arguments should be minimized. They tell us that the function can do more than one thing and it’s another parameter in the function signature.
Functions that take fewer arguments are better than the one that takes more. If it takes lots of arguments, consider combining them into a single object.
Finally, side effects should be avoided if possible. Functions with side effects do hidden things and are hard to test. Pure functions are much more testable and more predictable since they don’t commit side effects.
The post JavaScript Clean Code — Function Parameters and Side Effects appeared first on The Web Dev.
Posted on July 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.