In JavaScript, a function is a block of code designed to perform a particular task. Functions allow programmers can break up a large program into multiple smaller, more manageable components. As a result, there is no longer a need to write the same code repeatedly.
You can define JavaScript functions in many ways, two of them are: regular functions and arrow functions.
Regular Function in JavaScript
One of the basic way to define a function in JavaScript by using function
keyword.
// function declaration function greet(name) { return `Hey ${name}` } console.log('Sunil'); //Sunil // function expression const greet1 = function(name1) { return `Hey ${name1}` } console.log('Sunil'); //Sunil
You can call both function declaration and function expression as Regular Function in JavaScript.
Arrow Function in JavaScript
Arrow function was introduced in ES6. And it introduced a simple and shorter way to create functions.
const greet = (name) => { return `Hey ${name}` } console.log('Sunil'); //Sunil
While both the regular and arrow syntaxes define functions, when would you use one or another? That’s a good question.
Also Read: Arrow Functions in JavaScript
In this post, we will show you the difference between the two, so you could choose the right syntax for your needs.
Let’s discuss below 👇
Difference 1: this value
Regular function and this value
Inside a regular JavaScript function, this
value is dynamic.
The dynamic context means that the value of this
depends on how the function is invoked. In JavaScript, there are 4 ways you can invoke a regular function.
- Simple Invocation: this equals the global object or maybe
undefined
if you are using strict mode. - Method Invocation:
this
equals the object that owns the method. - Indirect Invocation:
this
equals the first argument. - Constructor Invocation:
this
equals the newly created instance.
//During a simple invocation the value of this equals //the global object (or undefined if the function runs in strict mode) // 1️⃣ Simple Invocation function simpleInvocation() { console.log(this); } simpleInvocation(); // Window Object //During a method invocation the value of this is the //object owning the method // 2️⃣ Method Invocation const methodInvocation = { method() { console.log(this); } }; methodInvocation.method(); // logs methodInvocation object //During an indirect invocation using //myFunc.call(thisVal, arg1, ..., argN) or //myFunc.apply(thisVal, [arg1, ..., argN]) //the value of this equals to the first argument // 3️⃣ Indirect Invocation const myContext = { value: 'A' }; function myFunction() { console.log(this); } myFunction.call(myContext); // logs { value: 'A' } myFunction.apply(myContext); // logs { value: 'A' } //During a constructor invocation using new keyword //this equals the newly created instance // 4️⃣ Constructor Invocation function constructorInvocation() { console.log(this); } new constructorInvocation(); // logs an instance of constructorInvocation
Arrow function and this value
Unlike regular functions, arrow functions don’t have their own this
binding. If we access this in the arrow function it will return the this
of the closest non-arrow parent function or in simpler term – it refers to its parent scope.
const user = { username: 'Sunil', getUserName: () => { return this.username; //here this refers to window object, //instead of user object }, }; const getName = user.getUserName; console.log(getName()); //undefined console.log(user.getUserName()); //undefined
The value of this in the arrow function is determined at the time of declaration and never changes. So call
, apply
, bind
cannot change the value of the arrow function this
.
Let’s see one more example:
var name = "Ram" let newObject = { name : "Hari", arrowFunc: () => { console.log(this.name); }, regularFunc() { console.log(this.name); } } newObject.arrowFunc(); // Ram newObject.regularFunc(); // Hari
So, this
inside an arrow function always refers to this from the outer context.
Difference 2: Constructors
Regular function and constructors
Regular functions are constructible, they can be called using the new
keyword.
function add (x, y) { console.log(x + y) } let sum = new add(2,3); // 5
Arrow function and constructors
However, arrow function can never be used as constructor functions. Hence, they can never be invoked with the new
keyword.
let add = (x, y) => console.log(x + y); const sum = new add(2,4); // TypeError: add is not a constructor
Difference 3: Arguments object
Regular function and Arguments object
In regular function, Arguments keywords can be used to access the arguments of which passed to function.
function regularFunction(a,b) { console.log(arguments) } regularFunction(1,2) // Arguments[1,2]
Arrow function and Arguments object
Arrow function do not have an arguments binding.
const arrowFunction = (a,b) => { console.log(arguments) } arrowFunction(1,2) //ReferenceError: arguments is not defined
However, if you want to access arguments in an arrow function, you can use the rest operator.
var arrowFunction = (...args) => { console.log(...args) } arrowFunction(1,2) // 1 2
Difference 4: Implicit return
Regular function and Implicit return
In regular function, you have to use return keyword to return any value. If you don’t return anything then the function will return undefined
.
//return keyword used function myFunction() { return 42; } console.log(myFunction()); // => 42 //return keyword missing function myEmptyFunction() { 42; } console.log(myEmptyFunction()); // => undefined function myEmptyFunction2() { 42; return; } console.log(myEmptyFunction2()); // => undefined
Arrow function and Implicit return
Arrow functions behave in the same way when returning values.
If the arrow function contains one expression, you can omit the curly braces, and then the expression will be implicitly returned.
const increment = (num) => num + 1; increment(41); // => 42 console.log(increment(41)); // => 42
increment()
consists of only one expression: num + 1
. This expression is implicitly returned by the arrow function without the use of return
keyword.
Difference 5: Methods
Regular function and Methods
Regular functions are the usual way to define methods on classes or objects.
In the following class Hero
, the method logName()
is defined using a regular function:
class Hero { constructor(heroName) { this.heroName = heroName; } logName() { console.log(this.heroName); } } const batman = new Hero('Batman'); batman.logName(); //Batman
Usually, regular functions as methods are the way to go.
Sometimes you would need to supply the method as a callback, for example to setTimeout()
or an event listener. In such cases, you might encounter difficulties accessing this
value.
For example, let’s use logName()
method as a callback to setTimeout()
:
class Hero { constructor(heroName) { this.heroName = heroName; } logName() { console.log(this.heroName); } } const batman = new Hero('Batman'); batman.logName(); //Batman setTimeout(batman.logName, 1000); // after 1 second logs "undefined"
After 1 second, undefined
is logged to console. setTimeout()
performs a simple invocation of logName
(where this is the global object). That’s when the method is separated from the object.
Let’s bind
this value manually to the right context:
class Hero { constructor(heroName) { this.heroName = heroName; } logName() { console.log(this.heroName); } } const batman = new Hero('Batman'); batman.logName(); //Batman setTimeout(batman.logName.bind(batman), 1000); // after 1 second logs "Batman"
batman.logName.bind(batman)
binds this value to batman
instance. Now you are sure that the method doesn’t lose the context.
Also Read: Understanding – Call, Apply, Bind
Binding this
manually requires boilerplate code, especially if you have lots of methods. There’s a better way: the arrow functions as a class field.
Arrow function and Methods
Thanks to the class fields feature you can use the arrow function as methods inside classes.
Now, in contrast with regular functions, the method defined using an arrow binds this
lexically to the class instance.
Let’s use the arrow function as a field:
class Hero { constructor(heroName) { this.heroName = heroName; } logName = () => { console.log(this.heroName); } } const batman = new Hero('Batman'); batman.logName(); //Batman
Now you can use batman.logName
as a callback without manual binding of this
. The value of this
inside logName()
method is always the class instance:
class Hero { constructor(heroName) { this.heroName = heroName; } logName = () => { console.log(this.heroName); } } const batman = new Hero('Batman'); batman.logName(); //Batman setTimeout(batman.logName, 1000); // after 1 second logs "Batman"
Difference 6: No duplicate named parameters
In normal function, we can do this:
// ✅ will work function add(a, a) {} // ❌ will not work 'use strict'; function add(a, a) {} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Arrow functions can never have duplicate named parameters, whether in strict or non-strict mode.
const arrowFunc = (a,a) => {} // Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Difference 7: Function Hoisting
Regular function and Function Hoisting
In regular function, function gets hoisting at top.
console.log(normalFunc()); //Normal Function function normalFunc() { return "Normal Function" } console.log(normalFunc()); //Normal Function
Arrow function and Function Hoisting
In arrow function, function get hoisted where you define. So, if you call the function before initialization you will get ReferenceError.
console.log(arrowFunc()); // ReferenceError: Cannot access 'arrowFunc' before initialization const arrowFunc = () => { return "Arrow Function" }
Conclusion
Understanding the differences between regular and arrow functions helps choose the right syntax for specific needs.
What other differences between the arrow and regular functions do you know?
Thanks for reading the article ❤️
Add comment