JavaScript is a very forgiving language. It’s easy to write code that runs but has mistakes in it.
In this article, we’ll take a look at some best practices for defining and using functions.
Never Declare a Function in a Non-Function Block
A function should never be defined in a non-function block like a if
block or within loops.
Functions within loops tend to cause errors since functions create a closure around the loop.
For instance, if we have the following code:
const funcs = []
for (var i = 0; i < 10; i++) {
funcs[i] = () => i
}
for (const f of funcs) {
console.log(f());
}
The code above would log 10 10 times because we declared the loop variable with var
, so the last value, which is 10, will be returned in each function that’s in the funcs
array.
We don’t want that, so we shouldn’t declare functions on the fly inside non-function blocks.
Use Function Expressions Inside Blocks
Function expressions can’t be used before they’re defined. This means that if they’re in blocks, then they can’t be used before they’re defined within the block.
Also, function declarations within blocks aren’t valid JavaScript syntax, so they shouldn’t be defined inside blocks.
If we want to define a function within a block, then we’ve to use function expressions.
For instance, we can define one within an if
block as follows:
if (true) {
const foo = () => {
console.log('foo.');
}
}
The code above is valid JavaScript syntax, so we used const
to define it so it’s only available within the block.
Therefore, we should remember to define functions as function expressions instead of using function declarations in blocks.
Never name a parameter arguments
We should never name function parameters with the name arguments
since arguments
is a special object that’s available within a traditional function.
If we have a parameter arguments
, then that’ll overwrite the value of arguments
with the value of the arguments
parameter.
For instance, if we have the following code:
function foo(arguments) {
console.log(arguments);
}
foo(1)
Then the value of arguments
is 1. On the other hand, if we have the following code:
function foo() {
console.log(arguments);
}
foo(1)
Then we get the actual arguments
object, which is:
Arguments [1, callee: ƒ, Symbol(Symbol.iterator): ƒ]
according to the console log output. It has the argument that we passed in and an iterator to iterate through the items or convert it to an array via Array.from
or the spread operator.
Therefore, we should name our parameter something other than arguments
.
Never Use arguments
, Use Rest Syntax ...
Instead
With the introduction of the rest operator, the arguments
object is pretty much useless.
The rest operator takes the arguments that we passed in and put them into an array if it has been assigned to a parameter. It works with both arrow functions and traditional functions
On the other hand, the arguments
object is only available within traditional functions and it’s an iterable array-like object rather than an array.
Therefore, to do anything useful with it, we’ve to convert it to an array, which is inconvenient.
For instance, instead of writing the following:
function foo() {
console.log(arguments);
}
foo(1)
We should instead write:
function foo(...args) {
console.log(args);
}
foo(1)
args
would be an array with value 1 inside it.
Likewise, we can use arrow functions as follows:
const foo = (...args) => {
console.log(args);
}
foo(1)
And we get the same result as before.
We can call any array method or do any array operations with rest parameters. For instance, we can write:
const addAll = (...args) => args.reduce((a, b) => a + b, 0)
addAll(1, 2, 3);
to add all the arguments that are passed and add them together with the reduce
method.
That’s something that we can’t do with the arguments
object directly.
Conclusion
Rest parameters are much better than the arguments
object for getting arguments that are passed into a function.
It works with both traditional and arrow functions.
We should never name a parameter with the name arguments
since it clashes with the arguments
object.
Declaring a function within a non-function isn’t valid syntax, so we shouldn’t do it.
We should only create functions as function expressions inside blocks.