Arrow Functions vs Normal Functions in JavaScript
January 24, 2026
Arrow Functions vs Normal Functions in JavaScript
Overview
JavaScript provides two ways to define functions: normal functions (function declarations/expressions) and arrow functions (introduced in ES6). While they may seem similar, they have important differences that affect how they work, especially regarding this, arguments, and constructor usage.
Key Differences
1. this Binding
Normal Function: Has its own this context that depends on how it's called.
var a = 100;
function NormalFunc(a, b) {
console.log(this.a); // For normal function, this.a prints the context's 'a'
console.log(arguments[0]); // Works here
}
const ArrowFunc = (...args) => {
console.log(this.a); // For arrow function, this.a still prints 100 (global)
console.log(...args);
};
NormalFunc.call({ a: "Ashish" }, 81, 82); // Prints "Ashish"
ArrowFunc.call({ a: "Debnath" }, 85, 86); // Prints 100 (arrow function doesn't have its own context)
Arrow Function: Doesn't have its own this. It inherits this from the enclosing lexical scope (parent context).
2. arguments Object
Normal Function: Has access to the arguments object.
function NormalFunc(a, b) {
console.log(arguments[0]); // Works - prints first argument
console.log(arguments[1]); // Works - prints second argument
}
Arrow Function: Doesn't have access to the arguments object. Use rest parameters instead.
const ArrowFunc = (...args) => {
// console.log(arguments[0]); // Error: arguments is not defined
console.log(...args); // Use rest parameters instead
};
3. Constructor Usage
Normal Function: Can be used as a constructor with the new keyword.
function Person(name) {
this.name = name;
}
const person = new Person("John"); // Works
Arrow Function: Cannot be used as a constructor.
const Person = (name) => {
this.name = name;
};
const person = new Person("John"); // TypeError: Person is not a constructor
4. Hoisting
Normal Function: Function declarations are hoisted.
sayHello(); // Works - prints "Hello"
function sayHello() {
console.log("Hello");
}
Arrow Function: Arrow functions (as function expressions) are not hoisted.
sayHello(); // TypeError: Cannot access 'sayHello' before initialization
const sayHello = () => {
console.log("Hello");
};
Summary Table
| Feature | Normal Function | Arrow Function |
|---------|----------------|----------------|
| this binding | Has its own this | Inherits this from parent scope |
| arguments object | ✅ Available | ❌ Not available (use rest params) |
| Constructor | ✅ Can use new | ❌ Cannot use new |
| Hoisting | ✅ Function declarations hoisted | ❌ Not hoisted |
| Duplicate parameters | ✅ Allowed | ❌ Not allowed |
| Implicit return | ❌ Requires return | ✅ Can return implicitly |
Function Constructor
There's also a third way to create functions using the Function constructor:
// Function Constructor
const myFunction = new Function("arg1", "arg2", "return arg1 + arg2;");
Important Note: Function constructor calls are not hoisted. You cannot call the function before it's defined in the code.
const x = 10;
function createFunction1() {
const x = 20;
return new Function("return x;"); // This |x| refers to global |x|
}
function createFunction2() {
const x = 20;
function f() {
return x; // This |x| refers to local |x| above
}
return f;
}
const f1 = createFunction1();
console.log(f1()); // 10 (global x)
const f2 = createFunction2();
console.log(f2()); // 20 (local x)
When to Use Each
Use Normal Functions When:
- You need
thisbinding to the calling context - You need to use the
argumentsobject - You need to create constructors
- You want function hoisting
- Working with object methods that need dynamic
this
Use Arrow Functions When:
- You want to preserve
thisfrom the enclosing scope - You're writing short, simple functions
- You're using array methods (map, filter, reduce)
- You want implicit returns for concise code
- You're working with callbacks and want lexical
this
Examples
Object Methods
const obj = {
name: "John",
// Normal function - 'this' refers to obj
greet: function() {
console.log(`Hello, I'm ${this.name}`);
},
// Arrow function - 'this' refers to global/window
greetArrow: () => {
console.log(`Hello, I'm ${this.name}`); // undefined
}
};
obj.greet(); // "Hello, I'm John"
obj.greetArrow(); // "Hello, I'm undefined"
Event Handlers
class Button {
constructor() {
this.clicked = false;
}
// Normal function - 'this' refers to button instance
handleClick() {
this.clicked = true;
}
// Arrow function - preserves 'this' from class
handleClickArrow = () => {
this.clicked = true;
}
}
Array Methods
const numbers = [1, 2, 3, 4, 5];
// Arrow functions are perfect for array methods
const doubled = numbers.map(n => n * 2);
const evens = numbers.filter(n => n % 2 === 0);
const sum = numbers.reduce((acc, n) => acc + n, 0);
Best Practices
- Use arrow functions for short, simple functions
- Use normal functions for object methods that need
this - Use arrow functions in array methods for cleaner code
- Use normal functions when you need the
argumentsobject - Use normal functions when creating constructors
- Be mindful of
thisbinding when choosing between them