Your first snippet, when run exactly as you show it, generates this error:
ReferenceError: test is not defined
Your second code snippet works because the module is initialized and const test
is defined. Then some time later, the route callback gets called and test
is now defined and has a value in the module scope.
For let
and const
, it is an error to reference them in running code before they are initialized. That's why the first code snippet generates a ReferenceError
.
But, once initialized, they are available anywhere within the scope in which they are defined. Some people call this hoisting to the top of the scope. Because this works differently than var
hoisting, I just think of it as once the const
or let
symbol is defined, it's available anywhere within its scope, even in code that might get called later, but appears earlier in the file. This is consistent with Javascript's run-time lookup for variables. When executing the route handler in your second code snippet, the symbol test
is looked up dynamically within its scope. Since const test
has already run and been initialized by the time the route handler gets called, it finds an already initialized test
variable and test()
works just fine.
In your first code snippet, the dynamic lookup for test
fails because const test
has not yet run when you try to call test()
and that creates the ReferenceError
.
You can see a demonstration of that a little more simply in this code which is pretty much the same as your second snippet:
function a() {
test();
}
const test = () => {
console.log(1+3);
}
a();
Here, we define function a
and then define const test
as a function. Then, we call a()
which in turn calls test()
. Since the sequence of events was:
- Define function a
- Define const test as a function
- Call
a()
- Which then calls
test()
And, all symbols are accessible from the module scope, then you can see that test
is defined before it is called so the code works.