-: [Jonas Schmedtmann] We talked about how execution contexts
are created and how that is related to variable
and function hoisting.
That was the first step of the creation phase.
So let's now talk about the second step,
the creation of the scoping chain.
So what does scoping even mean?
Scoping basically answers the question,
"Where can we access a certain variable or function?"
In Javascript each function creates a scope,
which is a space or an environment in which
the variables that it defines are accessible.
In many other programming languages a scope
is also created by if blocks, for blocks, or why blocks,
but not in Javascript.
So here in Javascript the only way that we have
to create a new scope is to write a new function
and this is something very very important in Javascript.
Also, in Javascript we have lexical scoping,
and lexical just means where
something is written in the code.
So it's about the position of somthing within our code.
Lexical scoping means that a function that is lexically
within and out of function,
which means that it's written inside of another function
and gets access to a scope of the outer function,
also called the parent function,
and with that it also gets access to the variables
and functions that the parent function defines.
Let's look at an example to get a better idea of this.
So here we have some more demo code.
In blue we have the global scope, which is a scope
that is accessible from everywhere, from all functions.
Like the global execution context,
you can think of the global scope as the default scope.
So in this scope we have access to the variable a,
and also to the first function.
In orange we have the scope of the first function,
which defines the variable b and the second function,
and this is a local scope, not a global scope.
And do you remember what I said about lexical scoping
in the last slide?
Thanks to lexical scope, this scope has also access
to it's parent scope and all the variables
functions that it defines.
In this case, that's the global scope.
Finally, in green, the scope of the second function.
In there we have a console.log statement,
which attempts to print values of variables a, b, and c.
Now, these variables are not defined in that function,
only c, but as we already know, a scope has access
to the scope of the function in which is sits lexically,
so when running this code, the Javascript engine
will not find variable b in the current scope, right.
So it just goes up and looks in the scope
of it's parent function and says, oh, here is b,
so let's use that.
The same happens for variable a.
It does not find it in the current scope
and also not in the parent scope,
so it goes even more up all the way to the global scope
and this is exactly called the scope chain.
Only if the Javascript engine does not find a variable
anywhere it throws an error and stops execution.
Now, it's very important to note that
this does not work backwards.
For example, the global scope will never ever have access
to the variables b or c unless we return
the values from the functions.
So locally scoped variables are not visible
to their parent scopes.
This is why I put that red arrow pointing upwards,
because that's the only direction the scope chain works.
Now how does this actually work behind the scenes?
Remember the execution context object
that we had been talking about before?
In the creation phase, each execution context object
will get exact scope chain,
which is basically all the variable objects
that an execution context has access to,
because, remember the variable object is what stores
all the defined variables and functions.
So in this example, in the second scope,
we have access to the variable object,
of the second functions, of the first function,
and to global variable object.
So what do you think will happen with this code in practice?
Let's go back to our text editor and find out.
So I have this code already here in my starter code,
which is the one that you will have available for download.
So here's the exact same code that
we had in the slide before.
So how exactly do you think this is gonna work?
I'll give you a minute to think about it.
Alright, so let's see the solution.
Great, so the console log actually printed
variable a, b, and c, so hey, hi, and hello, right?
Hey, hi, and hello.
So why does this work?
This works because, of course, the scoping chain
and in this case thanks to the scoping chain,
the second function has access to the variables
of the first function and of the global scope.
That's because the second function
is written inside of the first,
which in turn, is written inside of the global scope.
That's why we call it lexical scoping.
So another extremely important concept
and very much used in practice as well.
Alright, so we have seen that the concepts
of execution context, scope,
and scope chain are very related.
Now before moving on, I just want to make sure
that you really understand how these are related
and where the differences are.
So we saw it at the scope chain for each execution context
is stored in the execution context object,
but I just wanted to make very clear
that the execution stack is different from the scope chain.
So here we see another simple code example,
and we are already know
how the execution stack works, right?
So the first function gets called, which then calls
the second function, which in turn calls the third
function and for each of these calls,
a new execution context is put on top
of the execution stack.
Now, for the scope chain, things are a bit different.
We see that the global scope contains the a variable,
as well as the first and third functions.
Then the scope of the first function contains
the second function, and this is how I like to represent it.
Now let's remember that the execution stack
is the order in which functions are called,
but the scope chain is the order in which functions
are written in the code,
where they are lexically in our code.
So the order in which functions are called
does not determine the scope of the variables
within these functions.
What determines the scope of variables
is where the functions are written.
Since the third function is not in the scope
of the second function, of course it cannot access
variables b and c defined in the second and first functions.
It can only access the global variable a,
because the function is written in the global scope.
So, it's execution contexts that store the scope chain
of each function in the variable object,
but they do not have an effect on the scope chain itself.
This is important to understand.
So what do you think will happen in this case?
Let's go find out.
Let's first analyze the code that we have here.
So we have, in the global scope, the var a,
which is defined as hello,
we have the first function here and we have
the third function all in the global scope,
or in the global execution context.
Then, inside of the first function,
we have the b variable, we have the second function call,
and the function declaration.
We already know that because of hoisting we can
call the function before it's written here, right.
Okay and then in the third function,
we just define variable d and attempt
to console log variable c.
Alright so I'll give you a minute to think about
what's going to happen once I hit the refresh button here
in order to run this script.
Okay, so I'm going to do it.
I'm going to push the button.
Alright and so we have an error,
which tell us that c is not defined.
So two things here,
the first thing is why can the second function
even call the third function?
This buck here came from line 73, right,
which means that someone or somewhere,
this function was called,
which of course, was here.
So the third function was called from the second function
and this, once again, is possible because of scoping.
The second function has access to the third function
because of the scope chain, right?
So, the second function has access to the global scape
out here, in which the third function also sits lexically.
So the second function has the ability to call
the third function because of the scope chain
and there's nothing new here, right?
Now the second thing is why do we actually get
this error saying that c is not defined,
even though it was the second function
who called the third function?
The second function is the function that defined
the c variable and then called the third function,
but still the third function cannot access variable c,
which it is trying to print here,
which as we see resulted in this error.
So by now, I'm sure, that it's obvious to you that
the third function cannot access variable c because
the execution stack is different from the scope chain.
To answer the question, who can access the c variable,
the order in which the functions
were called does not matter.
All that matters is that the third function
is in a different scope than the second function
and so it cannot access variable c.
So which variables can the third function actually access?
Try to figure it out by yourself.
Did you get it?
Okay, so it's variable a and d.
Let's just test that.
I'm going to console log this one here
and then do console.log a plus d.
If I run this now, then I have hello John,
and hello is a and John is d.
So these are the only variables that we have access to
because they are in the scope chain for the third function.
Great, this is really cool, right?
You are already familiar which a lot of super important
and advanced Javascript concepts here and a lot of
important theory and I can absolutely guarantee you
that you already know a lot more
than a lot of Javascript beginners.
So this is amazing, right?
There's only one thing that we have left in this section,
and that is the "this" key word.
So let's go right to the next lecture
where we are going to talk about that.
See you there.