How to make your code better with intention-revealing function names

Cristian Salcescu
Apr 24, 2018 · 5 min read
Image for post
Image for post

Code is a way to communicate with developers reading it. Functions with intention-revealing names are easier to read. We read the function name and can understand its purpose. The function name is our tool for expressing intent on a piece of code.

Let’s look at a list of operations done in a functional style with the use of anonymous functions.

function getTodos(users){
return todos
.filter(todo => !todo.completed && todo.type === "RE")
.map(todo => ({
title : todo.title,
userName : users[todo.userId].name
}))
.sort((todo1, todo2) =>
todo1.userName.localeCompare(todo2.userName));
}

Now check the same functionality refactored using functions with intention-revealing names.

function isTopPriority(todo){
return !todo.completed && todo.type === "RE";
}
function ascByUserName(todo1, todo2){
return todo1.userName.localeCompare(todo2.userName);
}

function getTodos(users){
function toViewModel(todo){
return {
title : todo.title,
userName : users[todo.userId].name
}
}
return todos.filter(isTopPriority)
.map(toViewModel).sort(ascByUserName);

}

Function names give clarity to the code. With a good function name, you just have to read the name — you don’t need to analyze its code to understand what it does.

It’s widely estimated that developers spend 70% of code maintenance time on reading to understand it.

Kyle Simpson in Functional-Light JavaScript

Pure functions and closures

If you think about, we can split nested functions in two: closures and pure functions.

Pure functions don’t use variables from the outer functions. Besides, they return a value and have no side effects.

Closure functions use variables from the outer functions.

Pure function

A pure function is the best case scenario. It can be refactored out and given an intention-revealing name easily.

isTopPriority() and ascByUserName() are pure functions.

Closure

A closure function is more problematic. It may reference variables from different outer functions. We can start by refactoring it out to a named function, but keep it in the same parent function.

toViewModel() is a closure.

If you want to read more on closure, take a look at Why you should give the Closure function another chance.

Refactoring anonymous callbacks to named functions

A callback is a function passed as an argument to another function

Promise with anonymous callback

I’ll take the case of a promise with an anonymous callback.

function fetch() {
return dataService.fetch().then(newTodos => {
todos = newTodos;
eventEmitter.fire();
});
}

Below is the code after refactoring to a function with an intention-revealing name.

function setLocalTodos(newTodos){
todos = newTodos;
eventEmitter.fire();
}
function fetch() {
return dataService.fetch().then(setLocalTodos);
}

Event with anonymous callback

Now look at the case of an event handler written as an anonymous function.

$("#first").click(() => {
var list = getItemsFromList();
var filteredLIst = list.slice(0,1);
renderList(filteredLIst);
});

And this is how the code looks after refactoring to a function declaration with a clear name.

$("#first").click(renderFirstItem);function renderFirstItem(){
var list = getItemsFromList();
var filteredLIst = list.slice(0,1);
renderList(filteredLIst);
}

Function declaration defines a named function. The line of code starts with function .

Don’t use function names that add no extra value to the existing code. For example, don’t use names like onClick() or myCustomClickEvent(). The name should express what the code does, and a basic name may be used in other places.

Using a named function expression

Now let’s look at improving the readability of code using an anonymous IIFE.

IIFE means Immediately Invoked Function Expression. It’s a function expression that runs immediately after its definition.

(function(){
"use strict";
let $form = $("#editForm");
let persons = [];
$("#save", $form).on("click", () => {
let person = {
fname : $("#fname", $form).val(),
lname : $("#lname", $form).val()
};
persons.push(person);
});
})();

This time, I’ll just give an intention revealing name to the anonymous function.

(function renderPersonEditForm(){
"use strict";
let $form = $("#editForm");
let persons = [];
$("#save", $form).on("click", function savePerson() {
let person = {
fname : $("#fname", $form).val(),
lname : $("#lname", $form).val()
};
persons.push(person);
});
})();

renderPersonEditForm() and savePerson() are named function expressions.

Function expression defines a function as part of a larger expression. A function expression can be named or anonymous. The line of code, in this case, doesn’t start with function .

Refactoring to named functions will also make the debugging experience better.

Improving the debugging experience

Next, I’ll exaggerate the situation and use three anonymous IIFEs to make the point.

(function(){
/*code*/
(function(){
/*code*/
(function(){
/*code*/
})();
})();
})();

Below we can check the Call Stack. All we can see are anonymous functions.

Image for post
Image for post
Anonymous Function Expressions in the Call Stack

Anonymous functions will appear as (anonymous) in the CallStack

Now, look at the code written with named function expressions.

(function createTodoModule(){
/*code*/
(function getAllTodos(){
/*code*/
(function renderTodo(){
/*code*/
})();
})();
})();

This time, the Call Stack is easier to understand.

Image for post
Image for post
Name Function Expressions in the Call Stack

Using the Arrow Function

I think that giving a name can also improve readability in cases with little logic. Let’s take the example of computing a sum over an array.

//using anonymous function
let total = numbers.reduce((a, b) => a + b, 0);
//using the function declaration
function sum(a, b) { return a + b; }
let total = numbers.reduce(sum, 0);

As you can see, I refactored the anonymous arrow function to a function declaration as I find it easier to read.

If you prefer using the arrow function, you can give the function name using a variable.

let sum = (a, b) => a + b;
let total = numbers.reduce(sum, 0);

Sometimes the code is so small that the function name adds no value. In the next case, we stay with the anonymous arrow function until the codes become more complex. At that point only, we’ll refactor it out to a function declaration.

let completedTodos = todos.filter(todo => todo.completed);

Conclusion

I use named functions, instead of anonymous ones, because of three main reasons :

  • readability
  • better debugging experience
  • the option for self-reference, which will be used by recurring functions

Function names that express the intent make them much easier to understand and make it easier to change the code.

Image for post
Image for post

Frontend Essentials

Learn the fundamentals in JavaScript, Functional Programming, and Front-end Development.

Cristian Salcescu

Written by

Author of Functional JavaScript and Functional React book series. Enthusiastic about sharing ideas. https://www.amazon.com/gp/product/B08X3TPCQ8

Frontend Essentials

Learn more on functional programming concepts such as pure functions, immutability, closures, higher-order functions, or currying.

Cristian Salcescu

Written by

Author of Functional JavaScript and Functional React book series. Enthusiastic about sharing ideas. https://www.amazon.com/gp/product/B08X3TPCQ8

Frontend Essentials

Learn more on functional programming concepts such as pure functions, immutability, closures, higher-order functions, or currying.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store