Naming JavaScript Functions
- 3/9/2014
- ·
- #index
Remember that anonymous function? Probably not. After all, if it wasn’t worth a name, it probably hasn’t been used since.
But written one lately? Absolutely. They abound, especially in event handlers:
document.addEventListener('DOMContentLoaded', function () {
console.log('ready!');
});
And iterators:
[1,2,3,4].filter(function (n) {
return (n % 2) === 1;
});
Most of the time they’re, ahem, functionally harmless. But there are good reasons to give them a name.
Reuse
We recognize that the iterator above checks for odd parity. More complicated functions won’t be as clear, though. So rather than making a reader work back through the actual implementation, why not describe its behavior with a name?
function isOdd (n) {
return (n % 2) === 1;
}
[1,2,3,4].filter(isOdd);
This isn’t just clearer to read. By providing a name (or assigning to a variable) we’ve turned a one-off function into something we can reuse throughout a project. If we wanted to write a function for choosing prime numbers from a list, a first step might be to use isOdd
to filter out anything divisible by two. Now that it’s named, we can do that.
Clarity
Besides allowing us to reuse functions throughout a project, names can also help us understand what they represent. If we can establish a consistent convention we can guess at a glance how a function should be used. Consider the following rules:
is*
,has*
(isChocolate
,hasFrosting
) – truth tests around an object propertyto*
(toJSON
) – a conversion to another typeget*
(Cake.prototype.getType
) – retrieves a property from an objectset*
(Cake.prototype.setType
) – sets a property on an object
If we adhere to convention, we will immediately know that we can use isOdd
to filter collections of things; based on context we can make a reasonable inference that those things will be numbers and only odd ones will be returned.
Debugging
Stack traces indicate sources of error, but they’re infinitely more useful when the functions that are failing have names. For instance, running a purely-anonymous function with node’s --stack-trace-limit
set to 1:
(function () {
throw new Error('Whodunnit?');
})();
Produces an unhelpful trace:
Error: Whodunnit?
at repl:2:7
Contrast with the result once a name has been added:
(function isJudgeDoom () {
throw new Error('Whodunnit?');
})();
Error: Whodunnit?
at isJudgeDoom (repl:2:7)r>
Much better.
Trace summary
Each interpreter will present traces slightly differently, but all benefit from more information. Using the Node REPL as an example, contrast an error in an anonymous function with the following:
Definition | Trace |
---|---|
|
|
|
|
|
|
The best course, then, is to err on the side of caution: name early, name often.
Do it for clarity, do it for reuse. Do it to support debugging. But unless the function is the most trivial one-off, just do it.