Now that we explored try catch,
I wanna dive into a totally different,
yet slightly related concept.
I wanna talk about scoping of variables,
constants and functions.
Now what's scoping?
Scoping simply means that variables, constants and functions
can only be used in certain places.
Now for example, if I create a read file function
in errors.js, I can only use it in that file,
unless I export it with module exports.
Then we can export an object
and export our read file function like this if we want to.
That's what you saw before in the course already.
This is only the case in NodeJS, though.
In browser side JavaScript,
you can't use this module exports feature,
but there you also don't need to,
because in browser side JavaScript,
if you simply have a couple of script tags in your HTML file
that import different JavaScript files,
the variables and functions defined in those files
will be accessible in our files automatically.
So that file-based scoping,
locking in values into a file, is exclusive to NodeJS.
But we also have different forms of scoping
that apply to both the browser and NodeJS.
And the most important aspect here
or the most important thing to note about scoping here
is that constants and functions and variables
are only available in the block where you defined them.
Now what's a block?
A block in JavaScript is simply a section of code
that's wrapped by curly braces,
with the exception of the creation of an object.
If you use curly braces on the right side of an equals sign
to create an object,
then in here, we don't have a block of code.
Instead here, we're using curly braces to create an object.
In all our places in the code,
like here, when we define a function,
or if we try and catch, or if we have a for loop,
whenever we have curly braces here,
these curly braces mark a block of code.
A block of code that belongs to the loop,
a block of code that belongs to this function,
or a block of code that belongs to try, or catch.
And if I define a constant here
in this block here, for example,
it's only available in this block.
I can't console log file data here
outside of the try block.
If I try to do that,
and I then execute this errors.json file,
I get an error that file data is not defined.
Because here in line 9, file data is totally unknown.
The constant file data,
which I create in the try block,
is only available inside of that try block.
So console logging it here would work
if we wouldn't fail to read from the file system.
Console logging it here fails.
If we wanna access file data in both the try block
and in this line,
we instead have to create a variable file data
in the function block,
in which both the try block and line 10 are,
and then instead of creating a new constant
of that same name,
we simply assign file data here in try.
Because that's always important.
When I say that variables, constants and functions
are only available in the block in which they are defined,
that also means that they are available
in any nested blocks.
So here, file data is defined
in the read file function block.
That also makes it available inside of the try block though,
since the try block here is inside of that function block.
So from outside to inside is supported,
the other way around isn't.
And this concept, which is called block scoping,
is important to understand.
It is important to understand
that your constants and variables are only available
inside of the block where you define them,
not outside of that block.
Now directly related to this idea
of scoping variables and constants,
we also have the concept
of shadowing variables or constants.
And you can see that here, if I actually re-add const
in front of file data in line 6.
Now file data already is a variable defined in line 4.
And now instead of assigning to that existing variable,
I am creating a brand-new constant.
Now what will happen if we now try to execute this file?
Let's give it a try.
If I execute this, everything works,
but please note that we get undefined here.
And that's coming from line 10 actually.
Now we're getting undefined here
because file data in line 10
is actually referring to this variable file data
because they are in the same block of code.
File data defined here,
this constant file data which we are assigning in line 6,
is not the file data we are referring to here,
because file data here in line 6 is in a different block,
in that try block.
Now that's why we get undefined here
when we try to refer to file data.
It's simply referring to a different file data
which hasn't received a value.
But why aren't we getting an error
if we try to redefine a variable or constant
that already exists?
Typically that's not possible.
For example, I wouldn't be allowed
to add a constant file data in the very next line
after defining that variable with the same name.
Well that's again related to scoping.
If you do create variables or constants
with clashing names in different scopes, that's allowed.
Here in try, in a scope that's part of
this overall read file function scope,
I can create a new constant,
and if that constant has the name of a variable
or constant of the outer scope, that is allowed,
and we simply shadow that outer variable or constant.
That means we can use the outer variable or constant
if you want to, like in this case,
when I assign a value to it.
But we can also create a different constant
or variable of the same name,
which will then have a higher priority
inside of that inner block.
So then we simply have a second constant,
which is totally unrelated to this other variable
or constant with the same name.
This is something you can do.
It's also not uncommon to do that,
though if you find something like that to be confusing,
you can of course always go for different names
to make it clearer,
which variable or constant belongs to which scope.
Now here I will remove const again,
but being aware of this shadowing concept,
and that you can have clashing names
is something that you should keep in mind.
But that's already all you need to know about this feature.