Arrow Functions vs Regular Function

Joseph Perez
3 min readMar 27, 2021

Arrow functions and function declarations / expressions are not equivalent and have their own places within our code. Truly understanding the differences between the two can be challenging, but very useful for when we put it into practice. There are a few subtle differences that can help us distinguish the types.

Syntax is an easy identifier between the types of functions. In some cases, we can achieve the same result as regular functions by writing fewer lines of code with arrow functions.

// REGULAR FUNCTIONfunction add(x, y) {
return x + y;
}
add(2, 2) // 4
// ARROW FUNCTIONconst add = (x, y) => x + yadd(2, 2) // 4

As you can see, we were able to omit the return keyword and the curly brackets in this example. If we were expecting just one parameter, we could even leave the parentheses out and clean up our code just a little more.

const multiply = x => x * 2

One of the most important things to note when it comes to arrow functions is that they do not have their own this. Arrow functions do not redefine the value of this within their function body. This makes it a lot easier to predict their behavior when passed as callbacks and prevents bugs caused by use of this within callbacks.

In regular functions, this is bound to different values based on the context in which it is called. With arrow functions, however, this is lexically bound. It means that it uses this from the code that contains the arrow function. In other words, this means the same thing within the function body as it does outside of it.

Let’s take a look at an example:

// regular functionlet obj = {
id: 42,
counter: function counter() {
setTimeout(function() {
console.log(this.id);
}.bind(this), 1000);
}
};

In the example above, .bind(this) is required to help pass the this context into the function. Otherwise, by default this would be undefined.

// arrow functionlet obj = {
id: 42,
counter: function counter() {
setTimeout(() => {
console.log(this.id);
}, 1000);
}
};

In the example above, arrow functions can’t be bound to a this keyword, so it will lexically go up a scope and use the value of this in the scope in which it was defined.

Unlike regular functions, arrow functions do not have an arguments binding. Similar to the value of this, the arguments are resolved in the lexical scope. That means that inside an arrow function, the arguments refer to the arguments in the environment the arrow function is defined.

Regular function example:

function createObject() {
console.log('inside `createObject`:' this.foo);
return {
foo: 42,
bar: function() {
console.log('inside `bar`:', this.foo);
},
};
}
createObject.call({foo: 21}).bar();// inside `createObject`: 21
// inside `bar`: 42

Arrow function example:

function createObject() {
console.log('inside `createObject`:' this.foo);
return {
foo: 42,
bar: () => console.log('inside `bar`:', this.foo),
};
}
createObject.call({foo: 21}).bar();// inside `createObject`: 21
// inside `bar`: 21

Knowing when and why to use arrow functions can still be challenging and there is a lot of great documentation and videos online. We should feel encouraged to source information from many avenues so that we can get a deeper understanding, as it makes more sense over time. Here are a few more things to note and to look into for further understanding arrow functions vs regular functions:

  • Regular functions (created through function declarations / expressions) are both constructable and callable (can be called with or without new)
  • Arrow functions (and methods) are only callable. Class constructors are are only constructable.

--

--

Joseph Perez

I am a software engineer working in EdTech. I have a passion for supporting those working hard to get into the tech industry.