ESC

Type to search the knowledge base.

Arrow Functions Deep Dive

Lexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.

beginner3 min read
  • javascript
  • arrow-functions
  • this
  • functions

Arrow functions are not “shorter function.” They change this, arguments, new, and prototype. Use them where lexical this is the point — callbacks inside methods, Promise chains, array transforms. Skip them where you need a dynamic this or a constructor.

Syntax you’ll actually write

const add = (a, b) => a + b;
const square = (x) => x * x;
const makeUser = (name) => ({ name, active: true }); // parens for object literal

const sum = (...nums) => nums.reduce((a, n) => a + n, 0);

// block body needs explicit return
const parse = (raw) => {
  const data = JSON.parse(raw);
  return data.value;
};

Implicit return only works with a concise body. Returning an object literal requires ({ ... }) so { is not parsed as a block.

Lexical this (the whole point)

const timer = {
  label: 'poll',
  start() {
    this.id = setInterval(() => {
      console.log(this.label); // this === timer
    }, 1000);
  },
  stop() {
    clearInterval(this.id);
  },
};

A classic function callback would rebind this (often undefined in strict mode / timers). Arrows close over this from start.

const bad = {
  n: 0,
  // lexical this is the outer scope (module), not bad
  inc: () => {
    this.n += 1;
  },
};
bad.inc(); // does not update bad.n

Rule of thumb: object methods and prototype methods → function or method syntax. Nested callbacks → often arrows.

No arguments, no new, no prototype

const f = () => {
  console.log(typeof arguments); // ReferenceError in modules / strict free arrows
};
// use rest: (...args) => args

const Person = (name) => {
  this.name = name;
};
// new Person('Ada') → TypeError: Person is not a constructor

console.log((() => {}).prototype); // undefined

Rest parameters replace arguments. If you need new, use a class or a classic function.

Returning objects and async

const load = async (id) => {
  const res = await fetch(`/api/${id}`);
  return res.json();
};

// concise async
const loadName = async (id) => (await load(id)).name;

Async arrows work; generators cannot be arrows (function* only).

call / apply / bind and this

const show = () => this;
const obj = { x: 1 };
show.call(obj); // still outer this — bind is ignored for this

You can still partially apply args with wrappers; you cannot rebind an arrow’s this.

When to prefer classic functions

Need Use
Method with dynamic this obj.method() / class method
Constructor class or function
arguments object / generator classic / function*
Hoisted declaration function name() {}
Callback needing lexical this arrow
class Button {
  constructor(el) {
    this.el = el;
    // bind once, or use arrow field (per-instance function)
    this.onClick = this.onClick.bind(this);
    el.addEventListener('click', this.onClick);
  }
  onClick() {
    console.log(this.el);
  }
}

Interview answer

“Arrows use lexical this, have no arguments object, and cannot be constructed. I use them for callbacks inside methods so this stays correct, and method syntax for anything that should be called as obj.fn(). Implicit object returns need parentheses.”

Further reading

Related guides