Functions#
Functions are first-class values. JavaScript ships three forms
(function declarations, function expressions, arrow
functions), plus generators (function*) and async functions
(async function). Pick the form that fits
this semantics and the calling convention.
/concurrency`.
For methods and this binding inside classes, see
OOP.
Forms#
// function declaration; hoisted
function add(a, b) { return a + b; }
// function expression; not hoisted
const add2 = function(a, b) { return a + b; };
// arrow; lexical this, no arguments, no constructor
const add3 = (a, b) => a + b;
// method shorthand
const obj = {
add(a, b) { return a + b; },
};
// generator
function* counter() { let n = 0; while (true) yield n++; }
// async
async function fetchJson(url) {
const r = await fetch(url);
return r.json();
}
Pick the form by what binding the function needs.
Arrow functions capture
thisfrom the enclosing scope. Use them for callbacks (map, event handlers) and short expressions.Function declarations / expressions have their own
this, bound by the call site. Use them for methods (when the method shorthand inside an object literal is too far away) and constructors.Generators suspend with
yieldand resume on demand.Async functions return a
Promise;awaitinside them unwraps anotherPromisewithout nesting.then.
Parameters#
JavaScript does not enforce arity. Missing arguments are
undefined; extras are ignored (but available via the
arguments object in non-arrow functions or via a ...rest
parameter).
function greet(name = "operator") {
return `hello, ${name}`;
}
function log(level, ...msg) {
console.error(`[${level}]`, ...msg);
}
log("info", "started", 42);
// destructuring parameters
function listen({host = "0.0.0.0", port = 80} = {}) {
console.log(`${host}:${port}`);
}
listen({port: 8080});
The = {} default keeps listen() (no argument) from
destructuring undefined and throwing.
Returns#
return exits the function with a value; bare return or
falling off the end returns undefined. ASI means a newline
after return returns undefined; the operator keeps the
value on the same line.
function buildResponse() {
return // ASI inserts ; here
{ ok: true }; // unreachable
}
// returns undefined, not the object
Multiple returns you want together: return an array
([value, error]) or an object ({value, error}).
Closures#
A function value carries the bindings visible at its definition site. Those bindings are upvalues; they live as long as a closure that references them.
function makeCounter() {
let n = 0;
return () => ++n;
}
const next = makeCounter();
next(); next(); next(); // 1, 2, 3
Closures are how event handlers, promise chains, and partial application all work. They are the operator’s primary tool for private state in functional code.
This binding#
this in a regular function is set by the call site.
function show() { console.log(this.x); }
const obj = {x: 1, show};
obj.show(); // this = obj, prints 1
const ref = obj.show;
ref(); // this = undefined (strict) or global
show.call({x: 99}); // this = {x: 99}, prints 99
Arrow functions ignore the call site; this is whatever the
enclosing scope’s this is. This is what makes them right for
callbacks inside methods.
class Server {
constructor() { this.requests = 0; }
onRequest() {
setInterval(() => { this.requests++; }, 1000); // arrow captures `this`
}
}
Generators#
function* produces a generator function; calling it returns
an iterator that yields values one at a time.
function* range(start, stop, step = 1) {
for (let i = start; i < stop; i += step) yield i;
}
for (const i of range(0, 10, 2)) console.log(i);
Generators implement the iterator protocol, so they work
directly in for...of and spread.
const xs = [...range(0, 5)]; // [0, 1, 2, 3, 4]
Async#
async functions always return a Promise. await
inside them pauses the function until the awaited promise
settles, then resumes with its resolved value (or throws its
rejection).
async function fetchAll(urls) {
const responses = await Promise.all(urls.map(u => fetch(u)));
return Promise.all(responses.map(r => r.json()));
}
try {
const data = await fetchAll(urls);
} catch (e) {
console.error("failed:", e);
}
See Concurrency for the event loop, promise mechanics, and parallel patterns.
References#
Syntax for
functionand=>lexical surface.Variables for the closure-binding mechanism.
OOP for methods and
thisinside classes.Concurrency for
async/awaitand promises.