Lesson 56 of 80
A tail call is a recursive call that's the very last thing a function does — which in theory lets an engine skip growing the call stack. In JavaScript, almost no engine actually does this.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
A tail call is a function call that happens as the absolute last action in another function — nothing left to do with the result except return it. No multiplication after it, no concatenation, no wrapping it in another expression. Just return someCall(...) and nothing else.
Why anyone cares: when a call is in tail position, the current function's stack frame is no longer needed for anything. It's not going to do more work after the call returns — it's just going to hand the result straight back up. So an engine that recognizes this can, in theory, reuse the current stack frame instead of pushing a new one. A million tail calls deep would cost the same stack space as one call. This is called tail call optimization (TCO), and it's what lets some languages (Scheme, Elixir) write loops as recursion without ever blowing the stack.
// NOT a tail call — multiplication happens *after* the recursive call returns
function factorial(n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // work still pending: the multiply
}
// A tail call — the recursive call is the very last thing that happens
function factorialTCO(n, acc = 1) {
if (n <= 1) return acc;
return factorialTCO(n - 1, n * acc); // nothing happens after this returns
}
In factorial, the engine has to remember "multiply this by n" for every pending call — that's real stack growth, one frame per call, because each frame still has unfinished business waiting for its child to return. In factorialTCO, the multiplication happens before the call, folded into the accumulator argument, so the recursive call really is the last statement — there's nothing left for the current frame to do.
Try factorial(100000) in Node and it throws RangeError: Maximum call stack size exceeded. Try factorialTCO(100000) and — in a spec-compliant engine with real TCO — it would run in constant stack space. In practice, in every major JavaScript engine as of today, it still throws too.
acc above) instead of implicit in pending stack frames, which is often just cleaner code.Every recursive call pushes a stack frame the same way every function call does — this is the same call stack that underlies how JavaScript executes any function, recursive or not. And the accumulator pattern above — carrying state forward as an argument instead of reading it from an outer variable — is the opposite move from a closure, which keeps state alive by capturing it from an enclosing scope rather than passing it explicitly.