Proper Tail Calls

A specified stack-frame-reuse optimization for tail-position calls, implemented by Safari's JavaScriptCore but not V8 or SpiderMonkey — not reliable for stack safety in practice.

Purpose

A tail call is a function call in tail position — the final action before a function returns, with nothing left to do afterward. The specification defines an optimization (proper tail calls) letting such a call reuse the caller’s stack frame instead of growing the call stack.

Example

'use strict';
function sumTo(n, acc = 0) {
  if (n === 0) return acc;
  return sumTo(n - 1, acc + n); // tail position — the call IS the return value
}

Behavior notes

  • A call is only in tail position if it’s the literal, unmodified return value — return sumTo(n - 1, acc + n) + 1 is not a tail call, since something (+ 1) still happens to the result afterward.
  • The optimization requires strict mode in the specification’s own conditions.
  • As of this writing, only Safari’s JavaScriptCore implements proper tail calls — V8 (Chrome, Node) and SpiderMonkey (Firefox) do not, despite it being part of the standard since ES2015.

Common mistakes

  • Writing deep tail-recursive code expecting the stack-safety guarantee the specification allows, and hitting a stack overflow in Chrome, Node, or Firefox because the optimization simply isn’t implemented there.
  • Assuming “tail position” and “recursive” are the same thing — a tail call doesn’t have to be recursive at all; the property is purely about where the call sits, not what it calls.

Runtime notes

Specified by ECMAScript but inconsistently implemented — genuinely engine-dependent, not a guarantee to build stack-safety around in cross-browser or Node-targeted code.

Taught in these lessons