Last 30 Days
No notifications
Functions in JavaScript are *first-class* — you can pass them as arguments, return them, and store them in variables and arrays. This page covers the four ways to create them (function declarations, expressions, arrow functions, methods), the modern essentials (default params, rest, spread), and the two everyday gotchas: this binding and closures.
# Functions
function add(a, b) { return a + b; }
Hoisted to the top of its scope, so you can call it before its definition.const add = function (a, b) { return a + b; };thisconst add = (a, b) => a + b;
const sq = x => x * x; // single param, parens optional
const fn = () => ({ ok: true }); // wrap returned object literal in parensconst calc = {
add(a, b) { return a + b; }, // method shorthand
sub: (a, b) => a - b, // arrow as a property
};function greet(name = "world", punct = "!") {
return hello, ${name}${punct};
}
greet(); // "hello, world!"
greet("alice", "."); // "hello, alice."function sum(...nums) { // nums is a real array
return nums.reduce((s, n) => s + n, 0);
}
sum(1, 2, 3, 4); // 10const args = [1, 2, 3];
sum(...args); // 6
Math.max(...[5, 9, 1]); // 9arguments is an array-like of all passed args. Arrow functions don't have it — use rest ... instead.function apply(fn, x) { return fn(x); }
apply(sq, 5); // 25// returning a function
const multiplier = n => x => x * n;
const triple = multiplier(3);
triple(4); // 12
function makeCounter() {
let count = 0;
return () => ++count;
}
const c = makeCounter();
c(); c(); c(); // 1, 2, 3
The inner arrow keeps a reference to count even after makeCounter has returned. Closures are the foundation of private state, partial application, memoisation, and most React hooks.this — THE big differencethis based on how it's called (obj.fn() → obj, plain fn() → undefined in strict, new fn() → new instance).this — it captures the this from the surrounding lexical scope.const counter = {
n: 0,
bad: function () { setTimeout(function () { this.n++; }, 100); }, // ❌ this = undefined/window
good: function () { setTimeout(() => { this.n++; }, 100); }, // ✅ arrow inherits this from good()
};this — obj.fn = () => this captures the *outer* this, not obj.new.function*.(function () {
// private scope
const secret = 42;
console.log(secret);
})();
With ES modules you almost never need IIFEs anymore — every module *is* a private scope.