The Best Way to Create JavaScript Functions
Did you know there are four ways to create a function in JavaScript? Yeah, you read correctly, four ways. If you worked with JavaScript for a while, you probably saw all these approaches and wondered if there is any logic behind choosing a particular way to define a function in JavaScript. In this article, I will shed more light on this topic.
I mentioned earlier that there are four ways to define functions in JavaScript: function declaration, function expressions, arrow functions, and Function constructors. Let’s look at all of them and try to find out their pros and cons.
1. Function Declaration
This is probably the most used approach to define a function in JavaScript, and it looks like this.
// function declaration
function convertMetersToMiles(meters) {
return meters * 0.00062137;
}
let result = convertMetersToMiles(1000);
To define a function using this approach, you must start using the “function” keyword. Then you type the function’s name, in this case, convertMetersToMiles. If you have any parameters you need to provide, you can pass them between parentheses. In this example, there is just one. After that, there is the body enclosed in curly braces. It contains the logic of the function. This way of declaring a function is often used because it is very similar to how you define functions in other programming languages.
Hoisting
Function declarations are straightforward, but they do have one caveat. They are hoisted. If you are unfamiliar with hoisting in JavaScript, you can check out this article. Essentially, you can call a function defined this way before creating it. Here is an example of that.
// will work just fine and print 0.62137
console.log(convertMetersToMiles(1000));
function convertMetersToMiles(meters) {
return meters * 0.00062137;
}
Obviously, this is not a good coding practice. Not by a long shot, but you will code like this in real life projects. It will probably lead to a lot of bugs along the way, but hey, it does work. You can mitigate this by using a linter or by being disciplined when defining functions using the function declaration approach.
2. Function Expression
Another way to define functions in JavaScript is to use a function expression.
const convertMetersToMiles = function (meters) {
return meters * 0.00062137;
}
console.log(convertMetersToMiles(1000));
You can see why this style is called a function expression. We are assigning a function to a variable. In this case, we are assigning the conversion function to a variable called convertMetersToMiles. That was not the case for function declarations. There, we create a function, and no assignment is necessary.
Most of the time, a function defined in this way is anonymous. There is no name after the function keyword. So how do we invoke it? That is where the variable comes to the rescue. We can use the variable to execute the function. In our example, to get the result, we call convertMetersToMiles, passing 1000 as an argument.
This might seem like a strange way to create a function, but one advantage of this approach over function declarations is that hoisting does not occur. You are not allowed to call a function defined this way before you create it. And that is a good thing because it provides a built-in mechanism to minimize errors.
// Error -> Cannot access 'convertMetersToMiles' before initialization
console.log(convertMetersToMiles(1000));
const convertMetersToMiles = function (meters) {
return meters * 0.00062137;
}
3. Arrow Function
An arrow function in JavaScript is a compact alternative to a function expression with some limitations. An arrow function is compact, meaning it takes less code to write. And it has limitations, meaning there are scenarios where they are not a great choice. If the function you are trying to create returns a result and is a one-line function, then an arrow function will eliminate a lot of boilerplate code.
const convertMetersToMiles = (meters) => meters * 0.00062137;
Look how elegant our function became after we transformed it into an arrow function. You are not obliged to use this shorthand version. You can write the same function like this.
const convertMetersToMiles = (meters) => {
return meters * 0.00062137;
}
Arrow functions are great for eliminating excess code or passing a callback. However, they do have some limitations:
- Arrow functions do not have their own “this” binding, so you should not use them as methods or to handle browser events
- You can not access the arguments object inside an arrow function
- Arrow functions can not be used as constructor functions
Let’s see an example.
const converter = {
metersToMilesConstant: 0.00062137,
convertMetersToMiles: (meters) => meters * this.metersToMilesConstant,
};
// will return NaN because this referes to the Window/Global object
console.log(converter.convertMetersToMiles(1000));
4. Function Constructor
You can use a function constructor to create a new Function object from a list of arguments and a body provided as a string. This definition might seem a little odd, so let me show an example, and you’ll see that it makes perfect sense.
const convertMetersToMiles = new Function(
'meters',
'return meters * 0.00062137;'
);
console.log(convertMetersToMiles(1000));
To create a function object, I am passing in the parameters, in this case, a parameter named “meters” and the body as a string. I know it looks bizarre, but it works.
Why, in God’s name, would I ever use such a strange way to create a function? You
One reason you might want to take this approach is if you plan to create functions dynamically on the fly. You can build functions programmatically based on the runtime context. I think this is probably the only situation when you would use function constructors. And that’s because using this approach comes with some serious drawbacks.
- Code becomes much more error-prone. After all, you are creating functions from strings. Not a good idea.
- It’s not very safe to use this approach, security wise.
So, which is the best approach to create a function in JavaScript?
If you had the patience to read this article, you probably realized there is no “best” way. I would say that there is a “worst” way: to use Function constructors. Definitely avoid that.
You can choose arrow functions for simple functions or to pass anonymous functions as callbacks. This will eliminate boilerplate code and make your codebase easier to read. Otherwise, you can use a combination of function expressions and function declarations. Just be aware of hoisting.