JavaScript: Arrow Functions vs Regular Functions

Katherine
The Startup
Published in
5 min readOct 13, 2020

Once I started learning JavaScript I realized that functions are bread and butter of JavaScript programming language. Alongside classic function declarations there are arrow functions in JS (introduced in ES6). Arrow functions might be confusing in the beginning. In fact, arrow functions are great! They do look nicer, they do take less keystrokes, they are more fun, and they are so handy in React. Let’s take a closer look at their syntax and other differences compare to regular functions.

1. Syntax

Instead of the function keyword, arrow function uses an arrow (=>) made up of an equal sign and a greater-than character (not to be confused with the greater-than-or-equal operator, which is written >=).

// ES5 Regular function
let add = function(a, b) {
return a + b;
};
// ES6 Arrow function
let add = (a, b) => { return a + b };

It is important to note that arrow functions, introduced in ES6 in 2015, are anonymous, which means that they are not named. The example above shows the anonymous arrow function with two arguments a and b that returns a + b.

Arrow functions may appear unfamiliar and not very readable at first, but that quickly changes as the eyes get used to the structure.

They are very convenient for simple one-line actions, when we’re just too lazy to write many words.

One argument

If we have only one argument, then parentheses around parameters can be omitted, making that even shorter.

// ES5 Regular function
let double = function(n) { return n * 2 }
alert( double(3) ); // 6
// ES6 Arrow function
let double = n => n * 2;
alert( double(3) ); // 6

No arguments

If you have no arguments, here’s how you can go about it.

// ES5 Regular function
function sayHi() {
return alert("Hello!");
}
sayHi();// ES6 Arrow function
let sayHi = () => alert("Hello!");
sayHi();//Or
// ES6 Arrow function
let sayHi = _ => alert("Hello!");
sayHi();

NOTE: the difference is that () marks that there might be some default argument and _ says there will be, for sure, no defaults you care about.

Multiline arrow functions

Sometimes we need something a little bit more complex, like multiple expressions or statements. It is also possible, but we should enclose them in curly braces. Then use a normal return within them.

Like in the example below:

const power = (base, exponent) => {      // the curly brace opens a 
let result = 1; //multiline function
for (let count = 0; count < exponent; count++) {
result *= base;
}. // if we use curly braces, then
return result; // we need an explicit "return"
};

Another example:

let sum = (a, b) => {  // the curly brace opens a multiline function
let result = a + b; // if we use curly braces, then we need an
return result; //explicit "return"
}
alert(sum(5, 7)); //12

If you DO use {}, you must explicitly return the return value.

2. “this” binding

When discussing the arrow functions it is important to mention that unlike regular functions, arrow functions do not have their own this. Arrow functions don’t 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 classic function expressions, the this keyword 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.

For example, look at the setTimeout function below:

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

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

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

ES6 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.

3. Argument binding

Unlike regular functions arrow functions do not have an arguments binding. However, they have access to the arguments object of the closest non-arrow parent function. Named and rest parameters are heavily relied upon to capture the arguments passed to arrow functions.

The arguments is a reference of the name in the enclosing scope, for example:

function show() {
return x => x + arguments[0];
}
let display = show(10, 20);
let result = display(5);
console.log(result); // 15

The arrow function inside the showMe() function references the arguments object. However, this arguments object belongs to the show() function, not the arrow function.

4. Using “new” keyword

Regular functions created using function declarations or expressions are constructible and callable. Since regular functions are constructible, they can be called using the new keyword.

For example, the Car() function creates instances of a car:

function Car(color) {
this.color = color;
}
const blueCar = new Car('blue');
blueCar instanceof Car; // => true

Car is a regular function, and when invoked with new keyword, it creates new instances of Car type.

Not having this naturally means another limitation for arrow functions: the arrow functions are only callable and not constructible, i.e arrow functions can never be used as constructor functions. Hence, they can’t be called with new keyword.

If you try to invoke an arrow function prefixed with new keyword, JavaScrip throws an error:

const Car = (color) => {
this.color = color;
};
const blueCar = new Car('blue'); //TypeError: Car is not a
//constructor

Invoking new Car('blue'), where Car is an arrow function, throws TypeError: Car is not a constructor.

5. Duplicate named parameters

Regular function can have duplicate named parameters:

function add(a, a){}

However, it isn’t possible when using strict mode:

'use strict';
function add(a, a){}
// SyntaxError: duplicate formal argument a

Arrow functions can never have duplicate named parameters, whether in strict or non-strict mode.

(a, a) => {}
// SyntaxError: duplicate argument names not allowed in this context

Conclusion

You got familiar with two different styles for declaring functions: function expressions and arrow functions. Understanding the differences between regular and arrow functions helps choose the right syntax for specific needs.

Arrow functions excel when a simple change or operation needs to be used repeatedly. But they’re certainly used to write long, full functions too!

Arrow functions:

  • Do not have this
  • Do not have arguments
  • Can’t be called with new

That’s because they are meant for short pieces of code that do not have their own “context”, but rather work in the current one. And they really shine in that use case.

Resources:

📖 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

📖 https://developer.mozilla.org/en-US/docs/Glossary/IIFE

📖 https://www.w3schools.com/js/js_functions.asp

📖 https://www.w3schools.com/js/js_arrow_function.asp

📖 https://stackoverflow.com/questions/34361379/are-arrow-functions-and-functions-equivalent-interchangeable

📖 https://www.freecodecamp.org/news/when-and-why-you-should-use-es6-arrow-functions-and-when-you-shouldnt-3d851d7f0b26/

📖 https://eloquentjavascript.net/

📖 https://www.javascripttutorial.net/es6/javascript-arrow-function/

📖 https://javascript.info/arrow-functions-basics

--

--