This page will be covering how blocks and scope are implemented in environment diagrams - If you are fuzzy on what scope means and how to trace it in code, check out our page on scope!
Usually when we add a frame to the stack, we are showing that a function has been called and we list any variables that are within the scope of that function (i.e. parameters, variables declared inside the function body, etc.) For the most part, we'll have access to these variables throughout the function body...but what about when we don't?
Let's look at an example:
export let main = async () => {
let i = "Hello";
print(i); // 1
{
let i = "my precious";
print(i); // 2
}
print(i); // 3
//pause execution here
};
main();
Oh no! We have two i variables! How do we know which one is being printed at lines 1/2/3?
Let's make an environment diagram for the above example paused as the designated line...This is the syntax we will be using to address any variables that exist only in a nested block within a frame...this includes, if/else-if/else blocks, while loops, for loops, and general brackets.
Whenever we reach a print line, we use name resolution to decide which 'i' we should be printing! Line 1 prints "Hello", Line 2 prints "my precious", and Line 3 prints "Hello". Some notes:
The existence of a second variable with the same name is called Variable Shadowing...
Let's do one more example involving an if statement and a for loop:
let piratesLife = (): String => { //1
let arrgh = ["dont", "steal", "my", "booty"]; //2
for (let i = 0; i < arrgh.length; i++) { //3
let treasure = "booty"; //4
if (arrgh[i] === treasure) { //5
let i = "aye, we've found me treasure!"; //6
return i; //7
} //8
} //9
let i = "aye, we've got to keep looking"; //10
return i; //11
}; //12
//13
print(piratesLife()); //14
Before the frames are popped off the stack, our environment diagram will look like this:
arrgh is declared within the scope of the entire piratesLife function/frame <lines 1-11>. We will lose access to it only when the function ends
The first i (type: number, value: 0) and treasure both exist within the scope of the for loops <lines 3-8>...when i increments within the for loop, THIS is the i whose value will change!
The second i (type: string, value: "aye, we've found me treasure!") exists within the scope of the if-block <lines 5-7>...when i is returned inside the if-block (AKA when the booty is found), THIS is the i that will be returned.
The third i (type: string, value: "aye, we've got to keep looking") exists outside the for loop and the if-block, but within the scope of the piratesLife function <lines 1-11>...THIS is the i that will be returned when the for loop naturally ends (AKA when the booty is never found).