The Best Way to Create Functions in JavaScript

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.

Photo by Marc Mueller: https://www.pexels.com/photo/close-up-of-computer-screen-325111/

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 similar to how you define functions in other programming languages.

Hoisting

Function declarations are straightforward, but they do have one caveat. They are hoisted. 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;
}

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 many 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, we call convertMetersToMiles, passing 1000 as an argument to get the result.

This might seem strange 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;

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

You might want to take this approach 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 creating a function in JavaScript?

If you had the patience to read this article, you probably realized there is no “best” way. I would say 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.

Previous
Previous

How to Perform Multiple HTTP Requests in Angular

Next
Next

How to Pass Query Parameters to Angular Routes Using routerLink Directive