Arrow functions are a concise syntax for declaring functions introduced in ES6. They have lexical this
context and more compact notation.
Key differences from regular functions:
this
— inherit context from parent scopenew
arguments
Arrow functions are an alternative way to write functions that appeared in ES6 (2015). They use the =>
(“arrow”) symbol to define a function.
// Regular function
function add(a, b) {
return a + b;
}
// Arrow function
const add = (a, b) => {
return a + b;
};
// Short form (implicit return)
const add = (a, b) => a + b;
const greet = (name) => {
console.log(`Hello, ${name}!`);
return `Welcome, ${name}!`;
};
// Single expression — automatic return
const double = x => x * 2;
const sum = (a, b) => a + b;
const getMessage = () => "Hello, world!";
const sayHello = () => "Hello!";
const getRandomNumber = () => Math.random();
// With parentheses
const square = (x) => x * x;
// Without parentheses
const square = x => x * x;
// ❌ Wrong — interpreted as code block
const createUser = name => { name: name, age: 25 };
// ✅ Correct — wrap in parentheses
const createUser = name => ({ name: name, age: 25 });
const createUser = name => ({ name, age: 25 }); // ES6 shorthand
this
ContextThe most important difference — arrow functions don’t have their own this
.
const person = {
name: "Alexander",
age: 30,
// Regular function — own this
regularMethod: function() {
console.log(this.name); // "Alexander"
setTimeout(function() {
console.log(this.name); // undefined (this = window/global)
}, 1000);
},
// Arrow function — lexical this
arrowMethod: function() {
console.log(this.name); // "Alexander"
setTimeout(() => {
console.log(this.name); // "Alexander" (inherits this)
}, 1000);
}
};
// ✅ Works — function declaration is hoisted
console.log(regularFunc()); // "Works!"
function regularFunc() {
return "Works!";
}
// ❌ Error — arrow function is not hoisted
console.log(arrowFunc()); // ReferenceError
const arrowFunc = () => "Works!";
// ✅ Regular function — can be used with new
function Person(name) {
this.name = name;
}
const person1 = new Person("Alexander"); // Works
// ❌ Arrow function — cannot be used with new
const PersonArrow = (name) => {
this.name = name;
};
const person2 = new PersonArrow("Maria"); // TypeError
arguments
Object// ✅ Regular function — has arguments
function regularFunc() {
console.log(arguments); // [1, 2, 3]
}
regularFunc(1, 2, 3);
// ❌ Arrow function — no arguments
const arrowFunc = () => {
console.log(arguments); // ReferenceError
};
// ✅ Use rest parameters
const arrowFuncWithRest = (...args) => {
console.log(args); // [1, 2, 3]
};
arrowFuncWithRest(1, 2, 3);
Feature | Regular Function | Arrow Function |
---|---|---|
Syntax | function() {} | () => {} |
Hoisting | ✅ Yes | ❌ No |
this | Dynamic | Lexical |
new | ✅ Can | ❌ Cannot |
arguments | ✅ Has | ❌ No |
super | ✅ Has | ❌ No |
Prototype | ✅ Has | ❌ No |
Generators | ✅ Supports | ❌ Doesn’t support |
const numbers = [1, 2, 3, 4, 5];
// Old way
const doubled = numbers.map(function(n) {
return n * 2;
});
// Arrow functions
const doubled = numbers.map(n => n * 2);
const filtered = numbers.filter(n => n > 3);
const sum = numbers.reduce((acc, n) => acc + n, 0);
console.log(doubled); // [2, 4, 6, 8, 10]
console.log(filtered); // [4, 5]
console.log(sum); // 15
class Button {
constructor(element) {
this.element = element;
this.clickCount = 0;
// ❌ Problem with this in regular function
this.element.addEventListener('click', function() {
this.clickCount++; // this = element, not Button
console.log(this.clickCount); // NaN
});
// ✅ Arrow function preserves this
this.element.addEventListener('click', () => {
this.clickCount++; // this = Button instance
console.log(this.clickCount); // 1, 2, 3...
});
}
}
// Promise chain with arrow functions
fetch('/api/users')
.then(response => response.json())
.then(users => users.filter(user => user.active))
.then(activeUsers => console.log(activeUsers))
.catch(error => console.error(error));
// Async/await with arrow functions
const fetchUsers = async () => {
try {
const response = await fetch('/api/users');
const users = await response.json();
return users.filter(user => user.active);
} catch (error) {
console.error(error);
}
};
const calculator = {
value: 0,
// ✅ Regular function for methods
add(num) {
this.value += num;
return this;
},
// ❌ Arrow function — loses this
multiply: (num) => {
this.value *= num; // this doesn't point to calculator
return this;
},
// ✅ Arrow function inside method
processArray(numbers) {
return numbers.map(n => n + this.value); // this = calculator
}
};
const obj = {
name: "Alexander",
getName: () => {
return this.name;
}
};
console.log(obj.getName());
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
const timer = new Timer();
const numbers = [1, 2, 3, 4, 5];
const result = numbers
.filter(n => n > 2)
.map(n => n * 2)
.reduce((sum, n) => sum + n);
console.log(result);
const user = {
name: "Alexander",
greet() {
console.log(`Hello, ${this.name}!`);
const inner = () => {
console.log(`Inside: ${this.name}`);
};
inner();
}
};
user.greet();
const greetFunc = user.greet;
greetFunc();
const createMultiplier = (factor) => {
return (number) => number * factor;
};
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5));
console.log(triple(4));
const Person = (name) => {
this.name = name;
};
const person = new Person("Alexander");
console.log(person.name);
// 1. Array callbacks
const users = ['Alexander', 'Maria', 'Ivan'];
const greetings = users.map(name => `Hello, ${name}!`);
// 2. Short functions
const isEven = n => n % 2 === 0;
const getFullName = (first, last) => `${first} ${last}`;
// 3. Preserving this context
class Component {
constructor() {
this.state = { count: 0 };
}
handleClick() {
// Arrow function preserves this
setTimeout(() => {
this.state.count++;
}, 1000);
}
}
// 4. Higher-order functions
const createValidator = (rule) => (value) => rule.test(value);
// 5. Promises and async/await
const fetchData = async () => {
const response = await fetch('/api/data');
return response.json();
};
// 1. Object methods
const obj = {
name: "Alexander",
// ❌ Bad — loses this
greet: () => {
console.log(`Hello, ${this.name}!`); // undefined
},
// ✅ Good
greet() {
console.log(`Hello, ${this.name}!`);
}
};
// 2. Constructors
// ❌ Bad — doesn't work
const Person = (name) => {
this.name = name;
};
// ✅ Good
class Person {
constructor(name) {
this.name = name;
}
}
// 3. Prototype methods
// ❌ Bad
Person.prototype.greet = () => {
console.log(this.name); // undefined
};
// ✅ Good
Person.prototype.greet = function() {
console.log(this.name);
};
// 4. Event handlers with needed this
// ❌ Bad — if element this is needed
button.addEventListener('click', () => {
this.style.color = 'red'; // this is not button
});
// ✅ Good
button.addEventListener('click', function() {
this.style.color = 'red'; // this = button
});
// ✅ Use arrow for short functions
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(n => n * 2);
// ✅ Use regular for object methods
const user = {
name: "Alexander",
greet() {
return `Hello, ${this.name}!`;
}
};
// ✅ Use arrow for preserving context
class Timer {
constructor() {
this.time = 0;
setInterval(() => {
this.time++; // this = Timer instance
}, 1000);
}
}
Arrow functions are better for simple operations, while regular functions are better for complex business logic with multiple conditions and calculations.
// ✅ Arrow functions work great with destructuring
const users = [
{ name: "Alexander", age: 30 },
{ name: "Maria", age: 25 }
];
const names = users.map(({ name }) => name);
const adults = users.filter(({ age }) => age >= 18);
// Creating objects
const createUser = ({ name, age = 18 }) => ({
name,
age,
id: Math.random()
});
// Simple async arrow functions
const fetchUser = async (id) => {
const response = await fetch(`/api/users/${id}`);
return response.json();
};
// In arrays
const userIds = [1, 2, 3];
const users = await Promise.all(
userIds.map(async (id) => {
const user = await fetchUser(id);
return user;
})
);
// Error handling
const safeAsyncOperation = async () => {
try {
const result = await riskyOperation();
return result;
} catch (error) {
console.error('Error:', error);
return null;
}
};
// Components
const UserCard = ({ name, age }) => (
<div>
<h3>{name}</h3>
<p>Age: {age}</p>
</div>
);
// Event handlers
const Button = () => {
const [count, setCount] = useState(0);
// Arrow function preserves context
const handleClick = () => {
setCount(prev => prev + 1);
};
return <button onClick={handleClick}>{count}</button>;
};
// useEffect
useEffect(() => {
const timer = setInterval(() => {
console.log('Timer fired');
}, 1000);
return () => clearInterval(timer);
}, []);
// ❌ Error
const obj = {
name: "Alexander",
greet: () => console.log(this.name) // undefined
};
// ✅ Fix
const obj = {
name: "Alexander",
greet() {
console.log(this.name); // "Alexander"
}
};
// ❌ Error — interpreted as code block
const createUser = name => { name: name, age: 25 };
// ✅ Fix — wrap in parentheses
const createUser = name => ({ name: name, age: 25 });
// ❌ Error
const Person = (name) => {
this.name = name;
};
const person = new Person("Alexander"); // TypeError
// ✅ Fix
class Person {
constructor(name) {
this.name = name;
}
}
Arrow functions are a powerful ES6 tool that:
Advantages:
Limitations:
When to use:
map
, filter
, reduce
)this
contextthis
is neededUnderstanding arrow functions is the key to modern JavaScript and functional programming!
Want more interview preparation articles? Subscribe to EasyAdvice, bookmark the site and improve yourself every day 💪