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) + 1is 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.