Arrows, Functions, and My Sanity

November 20, 2025

A practical look at how I decide between arrow functions and function declarations. Cleaner code, fewer surprises, and a happier me.

Immediate returns

Arrow functions are great for returning things:

const labels = options.map((option) => option.label);

“Collect the label of every option. Got it.”

We can write the same algorithm with a conventional function:

const labels = options.map(function (option) {
  return option.label;
});

“Alright, we are mapping a function, so anything can happen here … Ah wait, it immediately returns a property. Just one more look to make sure I got that right … Okay, got it.”

I read a lot of code, so these little extra steps add up and drain me quicker than they need to.

If I return something immediately, I use an arrow function.

(Albeit at top-level, I prefer function foo() { return 42 } over const foo = () => 42.)

Binding this

I remember this giving me a hard time when I was getting started with JavaScript. Nowadays, I rarely (if ever) use this, so pardon this fairly synthetic example that hopefully still gets the point across:

function foo() {
  this.n = 42;

  return {
    double() {
      return this.n * 2;
    },
  };
}

What do you expect when calling foo().double()?

> foo().double()
NaN

Wait, what? Should it not be 84? Let’s try

function foo() {
  this.n = 42;

  return {
    double: () => this.n * 2,
  };
}
> foo().double()
84

Ok, that works. But why?

Arrow functions differ in their handling of this: they inherit this from the parent scope at the time they are defined. This behavior makes arrow functions particularly useful for callbacks and preserving context.

– MDN

We can still make this work without arrow functions:

function foo() {
  this.n = 42;

  const this_ = this;

  return {
    double() {
      return this_.n * 2;
    },
  };
}
> foo().double()
84

Again, I prefer code that is easier for me to grasp. Binding const this_ = this is an extra step that I have to resolve whenever I read this_. I prefer the arrow function.

If I need a certain this, I use an arrow function.

const vs function

Some time after arrow functions were introduced, people started slapping them everywhere:

const toggle = (b) => !b;

const Button = () => {
  return {
    label: "My button",
    onClick: toggle,
  };
};

That just makes my head go “Erm, what?”. When I think of something constant, it is things like

const foo = "bar";

const n = 42;

const products = [
  { name: "blueberry ice cream", price: "4.20" },
  { name: "dragonfruit", price: "6.00" },
];

I do not think of a function. Yes, a function (or the reference to one) is technically constant, it is just not what intuitively comes to my mind when thinking of a constant.

If something is a function, it should be denoted with function.

(Note that function foo(…) {…} gets hoisted, while const foo = function(…) {…} does not. But this is almost always what you want, anyway.)

Conclusion

Whatever piece of code I write, I will almost certainly read it again sooner or later, and when that time comes, my not-so-constant memory will have been read from and written to multiple times, so I’d better write this in the most intuitive way I can just so I will not have a harder time understanding it than need be.

When it comes to arrow functions and conventional functions, this is what is easiest to me:

  1. By default, denote a function with function.
  2. If the function immediately returns something and is not a top-level function, write it as an arrow function.
  3. If the function needs a certain this, write it as an arrow function.