A Practical Approach to JavaScript Function Forms

If you’ve looked at my React components, you’ll notice I freely mix arrow functions and function declarations. That stylistic choice tends to draw questions, so it’s worth walking through the reasoning behind it.

There are four primary function forms in JavaScript:

  1. Function declarations
  2. Function expressions
  3. Arrow functions
  4. Object methods

Each comes with its own semantics, but those details aren’t the focus here. The practical question is when to reach for which form.

Pre-Arrow-Function Rules

Before arrow functions existed, I developed a loose set of personal guidelines based on how the function was being used:

  1. Use a function expression when passing it as a callback.
  2. Use a function expression when assigning it to an object property.
  3. Use a function declaration everywhere else.

The core advantage of function declarations is hoisting of the function definition itself. That behavior was often the difference between working code and a runtime error:

thisWorks()

function thisWorks() {}

thisThrowsAnError()

var thisThrowsAnError = function () {}

These were never strict rules, and I wouldn’t argue for enforcing them via an ESLint configuration. They were just habits that avoided common pitfalls.

Rules After Arrow Functions

Once arrow functions and object methods became standard, I updated those loose guidelines:

  1. Use an arrow function when passing it as a callback.
  2. Use object methods for multi-line functions or functions with no return value; use arrow functions otherwise.
  3. Use an arrow function when implicit return or lexical this binding is valuable.
  4. Use a function declaration everywhere else.

Occasionally I’ll still write a single-line, returning function as a declaration simply so it’s easier to drop in a console.log or a debugger statement during development.

Context and Trade-Offs

These heuristics are not universal rules—different contexts call for different forms. For example, naming a function expression used as a callback can improve debugging output, which is a legitimate reason to deviate from the "arrow for callbacks" guideline.

The choice between function forms usually balances readability, debugging convenience, and the lexical semantics you need for a particular piece of code.

That’s it. Hopefully this clears up why the mix exists and offers a useful starting point for your own guidelines.