A function is a value that can be called. A declaration gives it a name; an arrow expression can make a small callback inline. Parameters are local bindings created for a call. return sends a value to the caller, and reaching the end sends undefined. Keep the difference between doing work and returning its result visible when reading callbacks.
const twice = value => value * 2;
const forgotten = value => { value * 2; };
console.log(twice(4));
console.log(forgotten(4));A braced arrow body is a block, so it needs return. To return an object literal from an expression arrow, wrap the literal in parentheses: value => ({value}). Default parameters apply to missing or undefined arguments; they do not automatically replace null. Treat defaults as part of the function's interface instead of scattering fallback expressions throughout its body.
Lexical scope means that the placement of the function in the source determines which outer bindings it can access. A closure keeps access to those bindings after the outer call returns. It does not freeze their initial values. If the captured binding changes, the closure observes the changed value. Each invocation of a factory can create a different binding with the same source-level name.
function makeLabel(prefix) {
let calls = 0;
return function(name) {
calls += 1;
return `${prefix}:${name}:${calls}`;
};
}
const first = makeLabel("A");
const second = makeLabel("B");
console.log(first("job"), first("job"), second("job"));Private mutable state is useful when its ownership is clear. Putting one counter at module scope would couple all callers. Keeping it inside each makeCounter invocation gives the returned function its own state. Validate a proposed update before storing it. If an overflow or invalid step throws, the next valid call should still begin from the previous successful value.
Callbacks also define an execution contract. For select, pass each original value and its index exactly once, in ascending order. Store the callback's result rather than invoking it again to decide whether it was truthy. A callback may record events, increment a count or throw; duplicate calls change observable behavior. A fresh result array can still contain the original object references, which is intentional here.
Practice by tracing two counters on paper and interleaving their calls. Next, write a predicate that records every (value,index) pair before returning true only for odd indices. Check both the selected values and the event sequence. These examples expose mistakes that a single happy-path return value would miss. The build asks for explicit results, independent closures and predictable failure behavior, all of which make later asynchronous callbacks easier to reason about.