Skip to content
{JS World}

Functions

Function declaration

Hoisted entirely (see hoisting) — can be called before the line it’s defined on.

console.log(add(2, 3)); // 5, works even though called before the declaration below

function add(a, b) {
  return a + b;
}

Function expression

Assigned to a variable; only the variable name is hoisted (not the function body), so calling it early fails.

console.log(subtract(5, 2)); // ReferenceError: Cannot access 'subtract' before initialization

const subtract = function (a, b) {
  return a - b;
};
// Named function expressions — the name is only visible INSIDE the function
// itself (useful for recursion), not in the outer scope
const factorial = function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
};

console.log(factorial(5)); // 120
console.log(typeof fact); // "undefined" — `fact` doesn't leak outside

Arrow functions

Shorter syntax, and — critically — no own this, arguments, or super; they inherit these lexically from the enclosing scope (see this-keyword).

const multiply = (a, b) => a * b; // implicit return, no braces needed
console.log(multiply(3, 4)); // 12

const square = (n) => {
  // explicit return needed once braces are used
  return n * n;
};

const sayHi = () => console.log("hi"); // no params still needs parens
// Arrow functions can't be used as constructors
const Person = (name) => {
  this.name = name;
};
new Person("Ada"); // TypeError: Person is not a constructor

// They also have no `arguments` object
const showArgs = () => console.log(arguments);
showArgs(1, 2, 3); // ReferenceError: arguments is not defined (in a module/strict context)

Default parameters

function greet(name = "Guest", greeting = "Hello") {
  return `${greeting}, ${name}!`;
}

console.log(greet()); // "Hello, Guest!"
console.log(greet("Ada")); // "Hello, Ada!"
console.log(greet("Ada", "Hi")); // "Hi, Ada!"
console.log(greet(undefined, "Hey")); // "Hey, Guest!" — undefined triggers the default, null does not

Rest parameters

Collects any number of arguments into a real array (see destructuring-spread-rest).

function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10

IIFE — Immediately Invoked Function Expression

A function that runs the instant it’s defined, used to create an isolated scope without leaking variables into the surrounding code — a pre-ES6-modules pattern for avoiding global namespace pollution.

(function () {
  const secret = "hidden from the outside world";
  console.log(secret);
})();

console.log(typeof secret); // "undefined" — secret never leaked out
// Arrow function IIFE
(() => {
  console.log("runs immediately");
})();

// Useful for top-level await before it was natively allowed at module scope
(async () => {
  const data = await fetch("/api/data");
  console.log(data);
})();

Quick comparison

// Declaration: hoisted fully, has its own `this`, works as a constructor
function greet1() {}

// Expression: only the variable is hoisted, has its own `this`, works as a constructor
const greet2 = function () {};

// Arrow: not hoisted (as a function), no own `this`/`arguments`, NOT a constructor
const greet3 = () => {};

Use a declaration for top-level named functions, an expression when you need to conditionally define or pass a function around, and an arrow function for short callbacks — especially ones that need the surrounding this (e.g. inside a class method or another callback).