The claim I've made about a non-hoisted JS being unable to support mutual recursion is just conjecture for illustration purposes. It's designed to help understand the need for the language to know about variables available in the scope(s). It's not a prescription for exact language behavior.
A language feature like hoisting -- actually hoisting doesn't exist, it's just a metaphor for variables being declared in scope environments ahead of time during compilation, before execution -- is such a fundamental characteristic that it can't easily be reasoned about when separated from the rest of the language's characteristics.
Morever, it is impossible to fully test this hypothesis in just JS. The snippet in the OP only deals with part of the equation, which is that it uses function expressions instead of function declarations to avoid function hoisting.
The language I was using to compare to for illustration is C, which for example requires function signatures to be declared in .h header files so that the compiler knows what a function looks like even if it hasn't "seen" it yet. Without it, the compiler chokes. That's a sort of manual hoisting in a sense. C does it for type checking, but one can imagine this sort of requirement existing for other reasons than that.
Another way of thinking about this is whether JS is a compiled language where everything has been discovered before it executes, or whether it is interpreted top-down in a single pass.
If JS were top-down interpreted, and it got to the definition of an a()
function that referenced a b()
inside it that it hadn't seen yet, that could be a problem. If that call expression was handled non-lazy, the engine couldn't figure out at that moment what the b()
call would be about, because b()
hadn't been processed yet. Some languages are lazy and some are non-lazy.
As is, JS is compiled first before execution, so the engine has discovered all the functions (aka "hoisting") before running any of them. JS also treats expressions as lazy, so together that explains why mutual recursion works fine.
But if JS had no hoisting and/or was not lazy, one can imagine the JS engine would be unable to handle mutual recursion because the circular reference between a()
and b()
would in fact mean that one of the two was always declared "too late".
That's really all I meant in the book.
a(1)
is called before function definitions. – Shamblina(1)
before function definition, it won't give the same error. – Shamblina(1)
invocation to the top you will get aTypeError: a is not a function
and this is very normal since now a, b and c are function expressions and they won't get hoisted to the top. But be careful don't repeat the test in the same browser window that you did a test with the first snippet where a, b and c were function definitions and they are kept in memory of course. Open up a new browser session.. – Adolphus