throw and Error Objects
In short: throw raises a value on purpose and stops execution right
there — nothing after it in that function runs, and control jumps to the
nearest catch (next lesson), or crashes the program if there isn't one.
You can throw anything, but the convention is to throw an Error
object, because it carries a .name and .message and a stack trace.
JavaScript ships several named error types — TypeError, RangeError,
SyntaxError and plain Error — and you can define your own by
extending Error.

Rejecting an out-of-range volume
A RangeError says the value's type was fine, but the number itself
was out of bounds:
throw stops setVolume dead at that line — the return level; below it
never runs for 150. Wrapping the call in try/catch (next lesson)
lets the program keep going instead of crashing outright.
Rejecting the wrong type entirely
A TypeError is for when the kind of value itself is wrong:
Naming the error type tells anyone reading the log, at a glance, what category of mistake happened — "got a number where a string was needed" — without reading the message text first.
A custom error type for your own app
You can extend Error to create error types specific to your program:
PlaybackError behaves exactly like a built-in error — it has a message,
a name, a stack trace — but a catch block, or a bug tracker, can tell it
apart from a TypeError or RangeError at a glance. instanceof Error
is still true, because it genuinely extends the built-in class.
Quick practice
What happens immediately when throw new Error("bad") runs inside a function, with no try/catch around it?
Which built-in error type best fits "the argument was the right value but outside the allowed range"?
Why throw new TypeError("...") instead of just throw "some text"?
