Exceptions
Statements normally run one after another. throw breaks that flow: it stops the running function at once and hands a value, usually an Error object, back down the call stack. If no code catches it, the script stops.
A normal call
A try block marks code that might throw. The global frame now remembers where to go if something does: the catch block on line 9. Then the engine runs the try block's lines in order.
Calling root(9) pushes a new frame onto the call stack. The frame holds the call's own variable, n = 9, and sits on top of the global frame until the call finishes.
9 < 0 is false, so line 2 does not throw. Line 3 returns Math.sqrt(9), which is 3. The frame pops off the stack, and console.log prints 3.
Throw and catch
Line 7 calls root(-4), so a new frame is pushed, this time with n = -4. The test on line 2 is now true: -4 is less than 0.
new RangeError("n < 0") makes an Error object in memory. Its name is "RangeError" and its message is "n < 0". Engines also store a stack trace in it: where it was made.
throw stops root at once: line 3 never runs. The engine unwinds the stack: it pops root's frame and looks for a try in the frame below. The global frame is inside a try, so the search ends there.
Control jumps to catch, skipping line 8. err is the thrown object itself. Line 10 prints its name and message: RangeError n < 0. The error is handled, so the script keeps running.
finally runs last, whether the try block finished, or threw and was caught. It would run even if catch threw again. It prints done, and the script carries on after line 13.
Uncaught and built-in errors
Call root(-4) with no try around it and nothing catches the error. The stack unwinds to the bottom, the script stops there, and the browser console reports Uncaught RangeError: n < 0.
The engine throws built-in errors itself: calling undefined() gives a TypeError, an undeclared name a ReferenceError, JSON.parse("{") a SyntaxError. All are Error objects, and try / catch handles them the same way.
What happens
// step 1/11 · flow
call stack one frame per function call still running
the script itself is the bottom frame
throw x stop now and pass x down the stack
try/catch a place where it can be caught
no catch the script stops: "uncaught" error
throw accepts any value, even a number or a string, but throwing an Error object is the convention: it carries a name, a message and a stack trace, and err instanceof Error is true. The figure draws the call stack as boxes. The specification calls each frame an execution context; the engine pushes one per function call and pops it when the call returns or is abandoned by a throw.