Functions: arrow vs declaration, and when each one wins
Two function shapes you'll see daily
// Declaration
function add(a, b) {
return a + b;
}
// Arrow expression
const sub = (a, b) => a - b;Both define a callable. Same behaviour for the common case. Three real differences matter:
1. Hoisting
Declarations are hoisted β you can call them before they appear in source order:
console.log(add(2, 3)); // 5, even though add is defined below
function add(a, b) { return a + b; }Arrow expressions are NOT hoisted. The const binding doesn't exist until the line that defines it. Try to call sub before its declaration β ReferenceError.
2. this
Inside an arrow, this is whatever it was in the surrounding scope. Inside a declaration (or function expression), this depends on how the function is called.
In modern code that uses arrow functions for callbacks, this rarely bites you β but it's the reason array.map(this.process) used to be broken and array.map(item => this.process(item)) worked.
3. Implicit return
Arrows with no {} block return their expression directly:
const double = n => n * 2;
const pair = n => ({ key: n, value: n * 2 }); // wrap object in ( ) to disambiguateCompact, idiomatic. The cost is that you can't add a debug log without converting to a body and losing the implicit return.
When to pick which
- Top-level utility / helper β declaration. Hoisting helps you organise files top-to-bottom by importance.
- Small callback or in-line transformation β arrow.
- A method on an object/class β declaration form (shorthand).