Arrow Functions

Understand arrow function syntax, implicit returns, and how arrow functions handle this differently from regular functions, along with the situations where they should not be used.

Arrow functions, introduced in ES6, offer a shorter syntax for writing function expressions and, more importantly, handle the this keyword differently from regular functions — a distinction that makes them ideal for some situations and unsuitable for others.

Arrow function syntax

An arrow function replaces the function keyword with a parameter list followed by =>. A single parameter can skip the surrounding parentheses (n => n * 2), though many style guides prefer keeping them for consistency; zero parameters or multiple parameters require parentheses.

Arrow functions are always expressions (never declarations), so they are typically assigned to a variable or passed inline as a callback, just like an anonymous function expression.

Implicit return

When an arrow function's body is a single expression written without curly braces, that expression's value is automatically returned — no return keyword needed. This is called an implicit (or concise) return.

If the body uses curly braces { }, it becomes a normal block body, and an explicit return statement is required to send back a value, exactly like a regular function — braces switch off the implicit return behavior.

Parameter variations

Arrow functions support the same parameter features as regular functions: default values, rest parameters, and destructuring. The parentheses around the parameter list can be omitted only when there is exactly one simple parameter and no default value.

• Zero parameters: () => "hello" • One parameter: n => n * 2 (or (n) => n * 2) • Multiple parameters: (a, b) => a + b • Default parameter: (n = 1) => n * 2

Lexical this

Regular functions get their own this, determined by how they are called (as a method, standalone, with call/apply/bind, etc.). Arrow functions have no this of their own at all — they capture this lexically, meaning they use the value of this from the surrounding (enclosing) scope where the arrow function was defined, permanently.

This makes arrow functions extremely useful inside methods when you need a callback (like inside setTimeout or an array method) that should still refer to the outer object's this, instead of getting its own, disconnected this.

Limitations compared with regular functions

Because arrow functions do not have their own this, they should not be used as object methods when that method needs to refer to the object via this — the arrow function would instead pick up this from whatever scope surrounds the object literal, which is usually not what is intended.

Arrow functions also cannot be used as constructors (new MyArrow() throws a TypeError), have no arguments object of their own (though rest parameters can substitute for this), and cannot be used with bind()/call()/apply() to change their this — since they simply do not have one to change.

Arrow function forms

No curly braces + no return keyword = implicit return of that one expression. Curly braces = block body, and you must write return explicitly to send back a value. To implicitly return an object literal, wrap it in parentheses: n => ({ value: n }) — otherwise { is parsed as the start of a block body.

const add = (a, b) => a + b;             // implicit return
const square = n => n * n;                // single param, no parens needed
const greet = () => "Hello!";              // no params
const logAndAdd = (a, b) => {              // block body needs explicit return
  console.log("adding", a, b);
  return a + b;
};

Code Examples

n => n * 2 is a concise arrow function passed directly to map: for each number n, it implicitly returns n * 2, with no braces or return keyword required. Expected result: [2, 4, 6, 8] n => n * 2 implicitly returns the multiplication result for every array element map calls it with.

The arrow function passed to setInterval has no this of its own, so it uses this from Timer's constructor scope — the actual Timer instance. A regular function passed to setInterval instead would have its own this (typically undefined or the global object in this context), breaking this.seconds++. Expected result: 1 2 3...(continues every second) this.seconds++ correctly refers to the Timer instance only because the arrow function inherits this lexically from where it was defined.

Common mistake: Replacing the arrow function with function() { this.seconds++; ... } would make this refer to something else entirely inside setInterval's callback, causing this.seconds++ to fail or silently create a global property instead of updating the Timer instance.

const numbers = [1, 2, 3, 4];
const doubled = numbers.map(n => n * 2);
console.log(doubled);
function Timer() {
  this.seconds = 0;
  setInterval(() => {
    this.seconds++;
    console.log(this.seconds);
  }, 1000);
}

const t = new Timer(); // logs 1, 2, 3, ... once per second

Lexical this: arrow function vs regular function

A regular function passed as a callback gets its own this (often not what you want); an arrow function passed as a callback keeps the this of the code around it.

const obj = {
  value: 42,
  regular: function () {
    setTimeout(function () {
      console.log(this.value); // undefined — `this` is NOT obj here
    }, 100);
  },
  arrow: function () {
    setTimeout(() => {
      console.log(this.value); // 42 — arrow inherits `this` from `arrow`'s scope
    }, 100);
  }
};

Important Rules

No braces + no return keyword means an implicit return of that single expression's value.

Braces around the body switch to a block body, requiring an explicit return statement.

Arrow functions do not have their own this — they always use this from their enclosing lexical scope.

Arrow functions cannot be used as constructors and have no prototype property.

Arrow functions have no arguments object of their own; use rest parameters (...args) instead if needed.

Avoid arrow functions for object methods that need this to refer to the object itself.

Where this shows up

Arrow functions are the default choice for short array-processing callbacks (map, filter, reduce), and are essential inside class methods or object methods whenever an inner callback (a timer, a promise handler, an event listener) needs to keep referring to the outer this instead of getting its own.

const user = { name: "Ana", greet: () => { console.log("Hi, I'm " + this.name); } }; user.greet();

const user = { name: "Ana", greet() { console.log("Hi, I'm " + this.name); } }; user.greet();

Because greet is an arrow function, this inside it does not refer to user — it is inherited from the surrounding (likely module or global) scope, where name is undefined. A regular method (or a regular function expression) correctly binds this to user when called as user.greet().

const makeObj = id => { id: id, active: true };

const makeObj = id => ({ id: id, active: true });

Without parentheses, { is interpreted as the start of a block body (not an object literal), so this arrow function returns undefined and { id: id, active: true } is treated as a labeled statement. Wrapping the object literal in parentheses fixes the ambiguity.

[Multiple Choice] What determines the value of this inside an arrow function? A. How the arrow function is called (as a method, standalone, etc.) B. The this value from the arrow function's enclosing lexical scope, fixed at the time it was defined C. It is always the global object D. Arrow functions cannot use this at all

B. The this value from the arrow function's enclosing lexical scope, fixed at the time it was defined — Arrow functions do not have their own this binding; they capture this lexically from the scope in which they were defined, and that value never changes based on how the arrow function is later called.

[True / False] An arrow function can be used as a constructor with the new keyword, just like a regular function. A. True B. False

B. False — False. Arrow functions cannot be used as constructors — calling one with new throws a TypeError, because arrow functions have no [[Construct]] internal method and no prototype property.

Rewrite this regular function as an arrow function with an implicit return: function isPositive(n) { return n > 0; }. Then write an arrow function makeUser that takes a name and returns an object { name } using implicit return.

const isPositive = n => n > 0; const makeUser = name => ({ name });

Remember: returning an object literal implicitly requires wrapping it in parentheses.