Execution Contexts
When a program runs, it builds up storage systems for holding variables and their values. These in-memory scope structures are referred to as 'execution contexts'.
These scopes differ to the lexical scopes in that they are built as the code executes (rather than as the code is typed). The rules surrounding in-memory scopes define which variables are accessible at different points throughout execution.As a program executes, it will be building up internal data stores for keeping track of all the variables that are available to different function objects. Each time a function is run, a new execution context is created as runs operate in isolation of previous runs. This means that for each lexical scope there could be anywhere between zero and many in-memory scopes created during execution. It just depends on how many times each function is run.
Using the adventure function example from the last post, here are the steps that a program takes when it runs:
var car = aCar();
var newJourney = function() {
var map = aMap();
var journey = function() {
var destination = aDestination();
log(car + journey + destination);
};
journey();
journey();
};
newJourney();
newJourney();
1.Interpreter sets up the execution environment, creating an in-memory global scope context to hold all the global variables.
2. At the first line of code, the interpreter builds a new key-value mapping inside of the execution context to keep track of the value bound to 'car'.
3. The value generated from aCar gets stored in the car variable
4. While the value being assigned to 'newJourney' is actually a function spanning across several lines, the interpreter ignores all of that code until that function is called and skips down to the first newJourney().
4. Running newJourney will create a new execution context, making room for new variables local to that function call.
5. The current execution context is now inside t he first call to newJourney, so the interpreter is now back up inside the function code.
6. A new variable is added to the current scope called 'map'.
7. Another new variable is added to the current scope called 'journey', which holds a function value.
8. The interpreter has reached the first call to journey(), which means it has to look up the journey variable and checks whether it has meaning in its current context (which it does), and then invoke the function.
9. Running journey creates a new execution and moves the interpreter's focus inside it.
10. Next, the interpreter has to lookup 3 more variables; car, destination and journey.It scans outward from the current context looking for the closest containing context that matches the names.
11. Now it has reached the end of the function, the focus jumps back out to where the function was called and resumes the execution (the second call to journey).
I won't go through the whole process a second time, but now the difference between in-memory scopes and lexical scopes should be a little more clear. The steps we just went through will repeat again, but this time creating a totally new execution context rather than just referencing the previous one. Variables created in this second run of journey will theoretically be stored in a different location from the first. What's more is that the values will be entirely different and would fail a '===' test comparing them.