Functions are important parts of JavaScript programs. They’re used for dividing code up into reusable chunks that do one thing.
Therefore in order to have clean JavaScript code, we have to have easy to understand functions.
In this article, we’ll look at how to write functions that are clean and easy to read and change. The most important thing is to write small functions.
Small Functions
Functions should be small. Smaller functions do less and it’s easier to read. Each line in a function should be around 100 characters long so they fit on any screen.
Doing less means less code means that it’s easier to read. If a function does more than a few things, then it should be divided into smaller functions.
Making small functions is very difficult in old JavaScript code since functions are used for many things they shouldn’t be used for like creating blocks and namespacing code.
However, now that we have JavaScript modules as a standard, we can gradually transform functions into doing things that functions are supposed to do, which is doing one thing.
For example, instead of creating blocks with functions as follows:
(function() {
var numFruits = 1;
})();
We can instead write:
{
let numFruits = 1;
};
They both create isolation, but instead of abusing the use of functions, we instead have just an isolated block of code. We can do this with ES6 or later.
let
and const
should be used to create block-level variables and constants respectively.
In addition, we can use modules if we want to put related pieces of code into a group. They can be used by importing the exported members of another module.
For example, we can create a file called fruit.js
that exports a member as follows:
export color = 'red';
Then we can import it in another module called main.js
as follows assuming that they’re in the same folder:
import { color } from './fruit'
console.log(color);
Then we get code isolation without using functions.
Blocks and Indent
Indentation should be automatically done with code formatters. We should have conditional statements and loops that are indented with 2 spaces.
Spaces are better than tabs because they don’t create issues with different operating systems. Tabs might look messed up in some systems.
We can use ESLine, JSLint or other linters to deal with indentation if we aren’t using a text editor that does formats JavaScript code automatically.
Do As Little As Possible
Usually, good functions should only do one thing. Long function is hard to read and if they have a lot going on, then it confuses the reader of the code.
The one thing may be hard to know. If it does more than one action, then it’s probably too much.
For example, code for rendering simple HTML to the user can be one function since that’s all it does.
However, if the HTML has many parts in it like looping through items retrieved from an API in multiple places and if statements, and so on, then they should split up into their own function.
If one function has lots of conditionals and loops, then they can probably be split into their own functions.
Another way to know if we can move something into its own function is that we can describe the piece of code without restating the implementation of the function.
One Level of Abstraction
Each function should only have one level of abstraction. This means if a function does something that has a high level of abstraction then it should only do that.
For example, if we want to write a function that loops through elements of an array and adds it to a list, then it should only do that.
Below is an example of dividing code into functions by the level of abstraction:
const addFruitLis = (fruits, ul) => {
for (const f of fruits) {
const li = document.createElement('li');
li.innerHTML = f;
ul.appendChild(li);
};
}
const addFruitUl = (fruits) => {
const ul = document.createElement('ul');
addFruitLis(fruits, ul);
document.body.appendChild(ul);
}
const fruits = ['apple', 'orange', 'grape'];
addFruitUl(fruits);
In the code above, we have a function addFruitLis
that create the li
elements and append it to the ul
element that’s in the parameter.
This is one level of abstraction because we’re adding the li
elements after the ul
element is generated. It’s one level below the ul
in terms of hierarchy.
Then we defined the addFruitUl
function to create the ul
element and delegate the addition of li
elements to the addFruitLis
function. Then the ul
is appended to the document’s body. This way, each function only does as little as possible.
Finally, we call the addFruitUl
function by passing in an array and then we get the elements on our page.
Each function only deals with one level of abstraction, as addFruitLis
only deals with the li
elements in the ul
element and addFruitUl
only deals with the ul
element.
The wrong way to write the code above would be to combine everything into one function. It makes the function’s code complex and confusing.
Conclusion
Functions should do a little possible. We can do this by isolating them in blocks and modules. Old code that uses functions to do that should be eliminated.
Also, each function should do as little as possible and only deal with one level of abstraction. Otherwise, the function’s code gets long and confusing.