Now it's time to see scoping in action
in a semi complex example,
to make sure that you really get
the super important concept.
And as always,
let's start by opening up a new VS Code window
with (mumbles) starter files.
So that's already here at my desktop.
And again,
I have the prettier configuration here.
The index is not really important here.
And so let's just start writing
some JavaScript code here.
And I'm gonna start by writing a
simple calcAge function.
And this time let's actually use a function declaration.
Not that it matters.
But just to have some diversity,
and not always create the same type of function.
So calcAge,
which is gonna receive a birth year.
And then all this function does
is to calculate an age.
And so this is nothing new at this point.
So the current year,
minus the birth year.
Then let's also
return that age here.
And so this calcAge function here is,
as we learned in the last lecture,
defined in a global scope.
And that's because it is
here in the top level code, right?
Also, this function here creates its own scope.
And that scope is gonna be equivalent
to the variable environment
of its execution context.
So that's all the stuff we learned
over the past couple of lectures.
And now let's create a global variable here.
So I'm gonna call that one
first, name.
And now let's actually call the function
so calcAge,
with 1991.
And then here inside of this calcAge function,
I will log first name to the console.
So first name.
Now as you see,
this first name variable is not actually in this scope
of the calcAge function.
However, it is a global variable
that we defined out here.
And so therefore,
through the scope chain,
it's gonna be made available
also inside of this scope.
So inside of this function, right?
So let's just execute this code here,
just to see that it actually works.
And then we're gonna explain a little bit more.
So live server.
And so here we go.
Let's open up the console.
Let's reload to get rid of this error.
And okay.
So you see that indeed,
Jonas here was printed to the console,
which is the first name variable.
And so when this line of code here was executed,
JavaScript did not find this variable in the scope.
And so it did a variable lookup,
where it looked up in the scope chain
to see if it found the variable there.
And indeed, the parent scope
of the calcAge function is the global scope.
And the first name variable is in there,
and therefore JavaScript could then use that.
Now here, if we tried,
for example just name,
then JavaScript would also do a lookup,
but it would not find this variable in the global scope.
And therefore, we would get an error.
Now with this variable,
actually we would get no error.
As I explained to you in the beginning,
this is kind of a special
variable name that we should not use.
So let's say I used last name.
And so again, this variable
is nowhere to be found.
So even by doing a look up
into the parent scope,
and so now when I saved this,
then I get this last name is not define error.
Okay?
And so going back here to first name,
then of course,
everything works just the same.
And that is true,
even though this variable here
was actually defined after the calcAge function.
But that's not a problem at all.
Because remember that the code in the function
is only executed once it's actually called.
And so that happens after
the declaration of the first name variable.
So right here.
And so at this point in the code,
the first name variable is already
in the global execution variable environment.
So in the global scope, ready to be used.
Okay, but let's actually get rid of this here.
And let's create another function inside of this function.
So function, printAge
and this will print a nice string to the console.
And as you also know already,
it also creates a new scope.
And let's create an output variable,
which is gonna be the string
that we will build here.
So output equals,
you are
the age,
and I forgot the curly braces
born in
birth year.
And then we simply log that message to the console.
And now to actually see this result here,
we need to, of course, call the function.
So let's do that right here.
So printAge.
And now we get a string with all the data
that we actually computed,
and it works just fine.
So we get the age that was calculated here,
and also the birth year
that was passed into this function.
And so here again,
we see the magic of the scope chain in action.
So once again,
the engine as it is executing this printAge function
is trying to access
or is trying to find the age variable
in the current scope,
however it cannot find it there.
And so therefore,
it goes to the parent scope,
where it will then find
this age variable of course,
that we created here.
And the same is true for the birth year variable,
because for scoping
the parameter of a function
work just like normal variables.
And also remember that we said
that the scope of a variable
is the entire region of the code
in which the variable is accessible.
So for example,
the scope of the age variable
is all of this area here.
So everywhere here,
the age variable is accessible.
So of course,
in the calcAge function, where it was defined,
and then also in all the child scopes,
so all the inner scopes, okay?
But of course, age is not accessible outside
of the calcAge scope.
So let me demonstrate that here.
So here, we will not be able
to access the age variable.
And that is because as I mentioned,
the scope chain is a one way street.
So only an inner scope
can have access to the variables of its outer scope,
but not the other way around.
So now here we are in the outer scope,
and we cannot have access to the variables
of a child scope,
which the scope of the calcAge function here
surely is, okay?
And the same goes for functions again.
And so we cannot call the printAge function out here,
for the same reason.
Let's comment out this one.
And so now we get printAge is not defined.
Okay?
So here in the global scope,
we do not have access to any variables defined
in any other scope.
Now, all right?
And here, let's actually take it to the next level,
and also include the name of the person.
So let's say, first name.
And let's try this now.
And indeed, it does have access
to the first name variable.
And so here,
the engine is doing an even bigger,
or a longer variable lookup.
So right now, we are of course,
in the printAge scope here.
And so then JavaScript cannot find a variable
in the current scope.
So it looks up in the scope chain,
which is then the next step,
the calcAge function.
However, it can also not find first name in this scope,
and therefore it goes up even further
into the global scope.
And there it is.
And so this is the value
that is gonna be used.
Okay, so I hope
all of this made sense until this point.
Let's now go further
and also create a block scope
right here in this printAge function.
So let's do an if statement here.
And we will check if the person
based on the birth year is a millennial.
And so that is if you were born
between 1981 and 1996.
Then you're a millennial,
and so let's check for that.
So birth year needs to be
at least 1981
and below,
below 1996.
And so here we are creating a block.
And therefore, this will be a new block scope.
And so let's now
define a variable here using const.
And I'm gonna call it string.
And let's say,
so first we get this string here to the console.
And then we can also say,
so if the person is a millennial,
we will say,
oh, and you're a millennial.
And then again, the first name.
And so now we are adding
even one more step here
to the scope chain,
because now the look up for first name
is even longer.
But it still will work.
So lemme log that to the console.
And so in this case,
I am clearly a millennial, okay?
And again, Jonas here is still found
all the way here
from the outer scope.
But now, what do you think will happen
as we try to log this variable
outside of this block scope?
So basically do the same,
but outside of the block.
So indeed, we get an error that str is not defined.
And again, that's because const and let variables
are block scoped.
So they are valid.
So they are available only inside the block
in which they were created.
But now watch as we create yet another variable,
which I will call millennial.
And let's set it to true.
And so this is an old pre ES6 variable.
And so what do you think will happen,
as we log it, also here outside of this block?
So we need to comment out this one.
So again, what do you think is gonna happen now?
And JavaScript can actually find it.
And so that is because of the fact
that var variables,
so variables declared with the var keyword
are function scoped.
So they simply ignore the block,
because they are not block scoped at all.
They're just function scoped.
And so here, we are still in the same function.
So here,
here we are trying to access the variable
that was defined in the same function, right?
So right now,
the scope of the millennial variable
is this entire function,
no matter if it was declared inside of a block or not.
Because again, var variables do not care
about blocks at all.
And so therefore,
we can then access the millennial variable
inside of its scope.
Okay?
So keep that in mind as you use,
or as you see a variable declared
with var, right?
Because, as I said,
on your own, you should probably not use
this variable yourself,
always just use const, or let.
But if you are reading other code bases,
or even working with older code,
then keep in mind
that this is how the var variable works.
And so that's why it's very important
to learn about str as well.
Okay, next up,
let's prove that functions are also in fact,
block scoped starting in ES6.
So let's just create a simple
function here, that will add two values.
Return A plus B.
And then let's
attempt to call it here outside of the block.
So add two and three.
And now we get add is not defined.
And so in fact,
the scope of this add function here
is only the block in which it was defined.
So only here,
we can use the add function.
And so that proves that
functions are now in fact, block scoped.
But remember that that is only true for strict mode.
And so that's the mode
that we're actually currently in.
But if I turn this off,
you should see now actually add being called.
So let's log the result of that to the console.
And so we should now see five.
And indeed, now it works.
Now, right?
But we should always use strict mode.
And so now we should get that error back.
And so lets also comment this out.
And so you can keep this here as a reference.
Okay.
And now to finish.
Let's do some experiments here.
So what do you think is gonna happen
if we declare a variable in this scope here?
So in this new block scope that already exists
in a parent scope?
So for example, let's say here,
const
first name, equals
Steven.
So what do you think this string here
will look like now?
So do you think that here,
it will say Jonas or Steven?
Well, let's try.
And in fact,
it now says Steven,
and that happens,
because as always,
JavaScript tries to look for the variable name
in the current scope.
And right now, it actually is in the current scope.
So first name is indeed, in this same block.
So in this same scope,
and so therefore, JavaScript will then use that variable
and not perform any variable look up
in the scope chain.
So the scope chain
isn't necessary at all,
if the variable that we're looking for
is already in the current scope.
And so that's exactly the case right here.
But of course,
then outside of this block,
the first name variable
is still gonna be the one
coming from the scope chain.
So that's why here,
you still see Jonas.
So right here in the printAge function,
which is another scope.
So it's the parent scope of this scope, right?
And so here, first name is still Jonas,
because it is not in the current scope of this function.
And therefore, JavaScript will make a variable lookup
in the scope chain,
until it can find first name,
which is still Jonas,
coming from down here.
So here we created a new variable
with the same name of another variable
that we had already created in a parent scope.
So we have two first name variables right now.
But that is not a problem at all.
Because in fact,
they are completely different variables,
they simply happen to have the same name.
But that's not a problem at all,
because they are defined in different scopes.
And so you can have repeated variable names.
That's absolutely no problem.
And that's also the reason
why you can have different functions
with the same parameter names.
Because again,
each parameter is only defined in that scope
of that function.
And therefore, it's not a problem at all
to have many functions with the exact same parameter names,
in the same way that it's not a problem
to have functions with the same variable names
inside of them.
But now watch what happens when we redefine a variable
from a parent scope inside of an inner scope.
So not creating a new variable,
but simply reassigning the value
of a variable.
So let's do that here.
And let's now say
output equals
new output.
And here we then have to change
the output to a let.
But let's just analyze what we did here.
So we are in the printAge scope.
So that's all of this here.
And in here, we have the output variable,
then we have a inner scope,
so like a child scope here,
which then redefines this output variable
from the outer scope.
And so if we then try to access that output here again,
what do you think is gonna happen?
Well, we get new output here.
And that is because we actually manipulated
an existing variable here,
inside of a child scope.
So inside of an inner scope, right?
We did not create a new variable,
we simply redefined a variable that we accessed here
from the parent scope.
So if we did create a new
variable called output here,
then we would have the same situation
as before with first name.
So this would then be a completely different variable,
and would not at all affect the output from the outer scope.
And so now as we save this,
then this new output should disappear.
And should be back to the original output
as we defined it in the scope now.
So indeed, now the new output is gone.
And again, that's because
it is now its own variable.
So a brand new variable,
which just happens to have the same name
as a variable from its parent scope.
But to illustrate this here,
let's actually leave it like this.
And I will also comment it,
creating new variable with
same name as
outer scopes
variable.
And let's put this together here.
Reassigning
outer scopes
variable.
So again, two completely different things.
But I think this is a very nice example
to once again illustrate
how the scope chain works.
And with this,
I hope that you now completely understand
how scoping works in JavaScript.
Also just keep in mind
that real code should (chuckles)
probably not be this confusing,
but this was just to show you how scoping works.