Understanding this in JavaScript: Arrow Functions vs. Regular Functions

Aditya Yadav
3 min readDec 27, 2024

When working with JavaScript, one of the most common sources of confusion is the behavior of the this keyword. The distinction becomes even trickier when comparing arrow functions and regular functions. Understanding how this works in these two types of functions is crucial for writing clean, bug-free code.

Let’s dive deep into the nuances of this, explore examples, and clarify how this behavior impacts your coding practices.

The Key Difference: Lexical vs. Dynamic Binding

Arrow Functions Bind this Lexically

Arrow functions don’t have their own this. Instead, they inherit this from the surrounding lexical context—the place where the function was defined. This is called lexical scoping.

Example:

const obj = {
value: 42,
arrowFunc: () => {
console.log(this.value); // `this` refers to the outer (global) context
},
regularFunc: function () {
console.log(this.value); // `this` refers to `obj`
}
};

obj.arrowFunc(); // undefined (in strict mode)
obj.regularFunc(); // 42
  • In the arrow function, this doesn’t refer to obj but to the global context (or undefined in strict mode).
  • In the regular function, this dynamically binds to obj when called as a method.

Regular Functions Bind this Dynamically

Regular functions have their own this, and its value is determined at runtime based on how the function is called:

  • As a method: this refers to the object the function is a property of.
  • As a standalone function: this refers to the global object (or undefined in strict mode).
  • In a constructor: this refers to the newly created object.

Example:

function regularFunc() {
console.log(this);
}

const obj = { regularFunc };
obj.regularFunc(); // `this` refers to `obj`
regularFunc(); // `this` refers to global object or `undefined`

Arrow Functions Shine in Callbacks

One of the primary reasons arrow functions exist is to simplify callback functions. Regular functions often require manual binding of this to ensure the correct context.

With Arrow Function:

function Timer() {
this.seconds = 0;
    setInterval(() => {
this.seconds++;
console.log(this.seconds); // Correctly logs the time
}, 1000);
}
new Timer();

With Regular Function (Binding Required):

function Timer() {
this.seconds = 0;
    setInterval(function () {
this.seconds++; // `this` is undefined without binding
console.log(this.seconds);
}.bind(this), 1000);
}
new Timer();

Arrow functions inherit this from the Timer function’s context, eliminating the need for .bind().

Prototype and Constructor Behavior

Arrow Functions Can’t Be Constructors

Arrow functions lack a prototype property, which means they can’t be used with the new keyword to create new objects. This is because they don’t have their own this.

Example:

const ArrowFunc = () => {};
function RegularFunc() {}

new RegularFunc(); // Works
new ArrowFunc(); // TypeError: ArrowFunc is not a constructor

Prototype Inheritance

Regular functions can be used for prototype inheritance due to their prototype property, but arrow functions cannot.

Practical Use Cases

1. Event Listeners

When using arrow functions as event handlers, you must be cautious because this doesn’t refer to the element that triggered the event.

Example:

const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log(this); // `this` refers to the global context, not the button
});

button.addEventListener('click', function () {
console.log(this); // `this` refers to the button
});

2. Object Methods

Avoid using arrow functions for object methods if you need this to refer to the object itself.

Example:

const obj = {
name: 'Example',
getName: () => {
console.log(this.name); // undefined
},
getNameRegular: function () {
console.log(this.name); // 'Example'
}
};

obj.getName(); // undefined
obj.getNameRegular(); // 'Example'

Summary: When to Use Each

Conclusion

Arrow functions are a powerful tool in JavaScript, designed to simplify code by lexically binding this. However, their behavior differs fundamentally from regular functions, especially when dealing with prototypes, constructors, or dynamic contexts.

Mastering the difference between the two will not only make your code cleaner but also help you avoid common pitfalls. Use arrow functions where simplicity and lexical scoping shine, and opt for regular functions when you need dynamic this behavior or prototype-based inheritance.

By understanding these distinctions, you’ll unlock a deeper level of JavaScript expertise and write code that’s both robust and easy to maintain.

--

--

Aditya Yadav
Aditya Yadav

Written by Aditya Yadav

Software Engineer who talks about tech concepts in web development

No responses yet