JavaScript Arrow Functions: Syntax, Return and Lexical this
Learn arrow function syntax, implicit return, how scope works, and why arrow functions keep this from the surrounding code, with real examples.
What you will learn
Modern JavaScript is full of this symbol: =>. It creates an arrow function, a shorter way to write a function. In this lesson you will learn the arrow syntax, how implicit return lets you skip the word return, a first look at scope (where variables can be seen), and the most important difference between arrow and regular functions: how they treat this.
From a regular function to an arrow function
Start with a function expression you already know:
const add = function (a, b) {
return a + b;
};
Step 1: remove the word function and put an arrow => between the parameters and the body.
const add = (a, b) => {
return a + b;
};
Step 2: when the body is a single expression, drop the braces and the return. The result is returned automatically:
const add = (a, b) => a + b;
console.log(add(5, 3)); // 8
All three versions do exactly the same thing. The last one is the shortest, and it is what most developers write for small functions.
Arrow function syntax
Many parameters: use brackets
const multiply = (a, b) => a * b;
One parameter: brackets are optional
const double = (n) => n * 2;
const triple = n => n * 3; // same thing, without brackets
console.log(double(4), triple(4)); // 8 12
Many teams always write the brackets for consistency, and that is perfectly fine.
No parameters: empty brackets are required
const sayHi = () => "Hi there!";
console.log(sayHi()); // "Hi there!"
A body with braces needs return
If you use { }, you are writing a normal function body, so you must write return yourself:
const grade = (marks) => {
if (marks >= 40) {
return "Pass";
}
return "Fail";
};
console.log(grade(75)); // "Pass"
Use braces whenever you need more than one statement.
Implicit return
Implicit return means the value is returned without the word return. It works only when there are no braces:
const isEven = (n) => n % 2 === 0;
const greet = (name) => `Hello, ${name}!`;
const total = (price, qty) => price * qty;
console.log(isEven(10)); // true
console.log(greet("Riya")); // "Hello, Riya!"
console.log(total(250, 3)); // 750
The trap: braces turn off implicit return
const add = (a, b) => { a + b }; // braces, but no return
console.log(add(2, 3)); // undefined
With { }, JavaScript expects a return. This is the number one arrow function bug.
Returning an object
Curly braces are also used for objects, so JavaScript gets confused. Wrap the object in round brackets to return it:
// Right: round brackets make it an object
const makeUser = (name) => ({ name: name, active: true });
console.log(makeUser("Riya")); // { name: "Riya", active: true }
Without the round brackets, JavaScript reads { ... } as a function body and gives a SyntaxError:
const makeUserWrong = (name) => { name: name, active: true }; // SyntaxError
Keep the arrow on the same line
You cannot put a line break between the parameters and =>:
const add = (a, b)
=> a + b; // SyntaxError
Arrow functions are always anonymous
Arrow functions do not have their own name, so you almost always store them in a variable (const add = ...) or pass them directly where they are needed. That leads to their favourite job.
Where arrow functions shine: short callbacks
A callback is a function you hand to another function, to be called later or many times. Arrow functions make callbacks short and readable.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4]
(map and filter belong to arrays, and you will learn them soon. For now, notice how little code the arrow needs.)
Compare with the long way:
const numbers = [1, 2, 3, 4, 5];
const doubledOld = numbers.map(function (n) {
return n * 2;
});
Other common places:
// A timer: run this once after 1 second
setTimeout(() => {
console.log("One second passed");
}, 1000);
// A button click
const button = document.getElementById("btn");
button.addEventListener("click", () => {
console.log("Button clicked");
});
A first look at scope
Scope means where a variable can be seen and used. Think of rooms in a house: things inside a room are not visible from outside, but from inside the room you can look out through the window to the hallway.
There are three main kinds of scope:
| Scope | Where it is created | Who can use the variable |
|---|---|---|
| Global | Outside all functions and blocks | Everyone, everywhere |
| Function | Inside a function | Only that function (and functions inside it) |
| Block | Inside { } of an if, for and so on |
Only that block (for let and const) |
const siteName = "MyCodeNest"; // global: visible everywhere
function showInfo() {
const lesson = "Arrow functions"; // function scope: only inside showInfo
if (true) {
const note = "Block scope"; // block scope: only inside this if-block
console.log(siteName, lesson, note); // all three are visible here
}
console.log(siteName, lesson);
console.log(note); // ReferenceError: note is not defined
}
showInfo();
console.log(lesson); // ReferenceError: lesson is not defined
Lexical scope: inner can see outer
A function can use variables from the places around where it was written. This is called lexical scope (lexical means “by position in the code”).
const taxRate = 0.18;
const addTax = (price) => price + price * taxRate; // uses taxRate from outside
console.log(addTax(100)); // 118
The reverse does not work: the outside cannot look into a function.
This is just the first step. A later lesson, Scope and Hoisting, goes deeper: shadowing, var, hoisting and the temporal dead zone.
What is this? (the short version)
Before the big difference, you need a tiny idea. Inside a function, the keyword this points to “the thing that is using me right now”. For a method of an object, this is that object.
const user = {
userName: "Riya",
greet() {
console.log("Hi, I am " + this.userName);
}
};
user.greet(); // "Hi, I am Riya"
When you call user.greet(), the object before the dot (user) becomes this, so this.userName is "Riya". (Objects and this have their own lessons soon. This is only what you need to understand arrow functions.)
Lexical this: the big difference
A regular function decides its this at the moment it is called. An arrow function has no this of its own. It uses the this of the code around it, from where it was written. This is called lexical this.
The problem with a regular function inside a method
const team = {
teamName: "Dev Team",
members: ["Riya", "Karan"],
showMembers() {
this.members.forEach(function (member) {
console.log(member + " is in " + this.teamName);
});
}
};
team.showMembers();
Output:
Riya is in undefined
Karan is in undefined
The callback function (member) { ... } is called by forEach, not by team, so its this is not team. (In strict mode, it would even crash with a TypeError.)
The fix: use an arrow function
const team = {
teamName: "Dev Team",
members: ["Riya", "Karan"],
showMembers() {
this.members.forEach((member) => {
console.log(member + " is in " + this.teamName);
});
}
};
team.showMembers();
Output:
Riya is in Dev Team
Karan is in Dev Team
The arrow function has no this of its own, so it uses the this of showMembers, which is team. Problem solved.
But do not use an arrow function as an object method
Because an arrow function does not get its own this, it cannot use the object it belongs to:
const user = {
userName: "Riya",
greet: () => {
console.log("Hi, I am " + this.userName);
}
};
user.greet(); // "Hi, I am undefined"
Here this is not user. It is whatever this was outside the object. So:
Rule: use a normal method for the object’s own methods, and use an arrow function for callbacks inside them.
Other differences from regular functions
| Regular function | Arrow function | |
|---|---|---|
Own this |
Yes (depends on how it is called) | No (uses the surrounding this) |
Own arguments object |
Yes | No |
Can be used with new (as a constructor) |
Yes | No |
Can be an object method that uses this |
Yes | No (avoid) |
Hoisted when declared with function |
Yes | No (it is stored in a const) |
| Short syntax and implicit return | No | Yes |
You do not need to memorise this table now. The first two lines matter the most.
Which one should I use?
- Arrow function: callbacks, short helpers, anything inside a method that needs the outer
this. - Regular function (declaration): main named functions, object methods, constructors, and code that must be callable before it is defined.
Many projects use arrows for almost everything, except object methods and constructors. Both styles are valid, so follow your team’s style.
Real-life use cases
1. Tiny helper functions
const square = (n) => n * n;
const isAdult = (age) => age >= 18;
const formatPrice = (amount) => "₹" + amount.toFixed(2);
console.log(square(6)); // 36
console.log(isAdult(17)); // false
console.log(formatPrice(499.5)); // "₹499.50"
2. Transforming a list
const prices = [100, 250, 400];
const withTax = prices.map((price) => price * 1.18);
console.log(withTax); // [118, 295, 472]
3. Filtering a list
const marks = [72, 38, 90, 45, 28];
const passed = marks.filter((mark) => mark >= 40);
console.log(passed); // [72, 90, 45]
4. Sorting numbers
const scores = [40, 100, 1, 5, 25];
scores.sort((a, b) => a - b);
console.log(scores); // [1, 5, 25, 40, 100]
5. Button click handler
<button id="btn">Click me</button>
<p id="output">Nothing clicked yet.</p>
const button = document.getElementById("btn");
const output = document.getElementById("output");
let clicks = 0;
button.addEventListener("click", () => {
clicks++;
output.textContent = `Clicked ${clicks} time(s)`;
});
6. A function that returns an arrow function
Arrow functions can build other functions:
const multiplier = (factor) => (number) => number * factor;
const double = multiplier(2);
const tenTimes = multiplier(10);
console.log(double(7)); // 14
console.log(tenTimes(7)); // 70
Read (factor) => (number) => number * factor as: “give me a factor, and I give you a function that multiplies by it.” This idea powers many advanced patterns, and you will see it again in the closures lesson.
7. Chaining several steps
const orders = [120, 80, 450, 30, 260];
const total = orders
.filter((amount) => amount >= 100)
.map((amount) => amount * 0.9)
.reduce((sum, amount) => sum + amount, 0);
console.log(total); // 747
This keeps orders of ₹100 or more, gives each a 10% discount, and adds them up. Do not worry about reduce yet. Arrow functions are what keep this readable.
Common mistakes
- Using braces without
return.(a, b) => { a + b }returnsundefined. Remove the braces or addreturn. - Returning an object without round brackets. Write
() => ({ name: "Riya" }). - Using an arrow function as an object method that needs
this. It will not see the object. Use a normal method. - Using an arrow function with
new.new add()throws aTypeError. Arrow functions are not constructors. - Expecting
argumentsinside an arrow function. Arrow functions do not have it. Use rest parameters (a later lesson). - Putting a line break before
=>. It must be on the same line as the parameters. - Writing very long one-line arrows. Short is good, but if it is hard to read, use braces and several lines.
- Calling an arrow function before it is created. It is stored in a
const, so the same rule applies as for function expressions. - Forgetting that a function declared with
constcannot be reassigned.const add = ...; add = ...is an error.
Practice
-
Rewrite this function as an arrow function with implicit return:
function double(n) { return n * 2; } -
Write an arrow function
fullName(first, last)that returns"first last"using a template literal. -
Write an arrow function
isLongWord(word)that returnstrueif the word has more than 5 letters. -
Write an arrow function
makeProduct(name, price)that returns an object withnameandpricekeys. Remember the round brackets. -
Fix the bug:
const cube = (n) => { n * n * n }; console.log(cube(3)); // prints undefined -
Use
.map()with an arrow function to turn[1, 2, 3, 4]into[1, 4, 9, 16]. -
Use
.filter()with an arrow function to keep only the words longer than 3 letters from["cat", "horse", "dog", "tiger"]. -
Click Practice in Editor and change the button code so that the paragraph shows a different message on each click, for example the click count in capital letters.
-
Explain, in your own words, why the arrow function in
showMembers()can usethis.teamNamebut the one inuser.greetcannot usethis.userName. -
Challenge: write
const adder = (a) => (b) => a + b;. What doesadder(3)(4)return, and what doesadder(3)return?
Recap
- An arrow function is a shorter function:
(a, b) => a + b. - One parameter: brackets optional. No parameters: write
(). Many: use brackets. - Without braces, the result is returned automatically (implicit return). With braces, you must write
return. - To return an object directly, wrap it in round brackets:
() => ({ ... }). - Arrow functions are perfect for callbacks (
map,filter,setTimeout, event listeners). - Scope decides where a variable is visible: global, function and block. Inner code can see outer variables, but not the other way round.
- Arrow functions have no
thisof their own. They use thethisof the code around them (lexicalthis), so they are great inside methods and bad as methods. - Arrow functions have no
argumentsobject and cannot be used withnew. - Next you will go deeper into functions: anonymous and named functions, first-class functions, IIFE, and pure vs impure functions.