Scope! is a crucial topic to get the hang of as a Programmer, it is a concepts in programming that applies to any programming language, and it also reveals the magic behind the scenes of how language Compilers and Engines carry out some of their duties, my Goal here is to explain in simple terms, whilst still revealing the technical parts you need to understand. Let's get to it! :)
Table of Content
Overview of Scopes
The Models of Scopes
Lexical Scopes
Conclusion
Overview of Scopes
In simple terms, scopes help the Compiler and Engine to identify where each variable/identifier was created in our program and retrieve their values when needed. yeah, that's it!
The word Identifiers are names you assign to elements in your program eg. (Variables, Arrays, Functions, etc.)
As simple as it sounds, scopes can also get complex, cause it works directly with the Compiler and the Engine, <- these guys need scopes to carry out their work.
// Global Scope
const name = "John Doe";
// Function Scope
function calculateAge(year){
let currentYear = 2021;
return currentYear - year;
}
// Block Scope
for(let i = 0; i < 5; i++){
console.log(`Log ${i}`);
}
in the code above you'd find three types of scopes
Global Scope (Notice the variable/identifier is not within any curly braces)
Function Scope
Block Scope
Here's a metaphor, think of an environment like your room, you'd surely find things in there; like a bed and probably a table right? but immediately you go outside you won't see what was in there anymore.
Because your brain has associated those things that make your room unique to that specific place in your house.
When our programs get executed the Compiler or Engine keeps record of every variable/identifier but associates them to an environment, either the Global, Function or Block environment so it can easily get their values when it wants them.
So think of Scopes as environments in our programs, let's take a look at each type of Scope.
Global Scope
the global scope is the most loosed environment, when identifiers can't be found in the other types of scope the compiler usually will end it's search in the global scope.
Function Scope
Function scope is just any environment that is a function, hence the name.
Block Scope
Any other environment that is not the Global Scope or Function Scope is going to be the block scope, usually would have curly braces.
Models of Scope
Scopes are classified, and it totally depends on the programming language you're coding in, but we're going to use JavaScript as an example, these classification is what is referred to as the Models of Scope.
Lexical Scope
Dynamic Scope
Lexical Scope
Lexical scope is the most common in programming languages, the term Lexical compliments the fact that JavaScript actually gets Compiled to some sort before the Engine executes the compiled result, I'm going to write on this whole fact soon, but for now, just know that the compilation happens so quickly, and the engine executes it immediately.
The First process it goes through while compiling is called Lexing/Tokenizing which is where we get the term "Lexical" from Lexical Scope.
Briefly, Lexing/Tokenizing is taking each expression in our program and then breaking them into a String of sequence (I will show you what that looks like) according to their meaning in any specific language in this case JavaScript.
// An Expression
let age = 10;
// Tokenzied
"['{declaration: let}',
'{identifier: age}',
'{operator: =}',
'{literal: 10}']";
This code above shows how an expression will mostly look when Tokenized.
So in simple terms a communication happens between the Compiler and the environment or Scope containing identifiers, this happens within that First process of compilation (Lexing/Tokenizing).
The idea behind Lexical scope is that the environment where the identifiers live is created when the function or block was originally written.
Look-Up
Another term we have to get used to is the word Look-Up.
When the compiler starts compiling our JavaScript code it needs to understand how each variable should be accessed so it doesn't get confused, so it depends on each environment to provide its identifiers.
function myFunc(){
const paragraph = "I am a simple";
function concat(){
return `${paragraph} + paragraph.`
}
return concat();
}
myFunc();
When "myFunc" get's called in the code above, our Compiler first will identify the Scope of "myFunc" in this case it is the Global Scope, then it moves within the function and continues executing,
then it looks for the first variable/identifier it can find in the "myFunc" in this case it would be "concat" because of a concept called Hoisting in JavaScript (let's just continue though) and it's scope would be Function Scope.
When "concat" get's called it goes into the body of the function and repeats the step until it identifiers all the Variable/Identifiers scopes.
upon each look up if the compiler cannot get an identifier within an environment it goes up the parent of the environment to look up for that identifier if it still couldn't get it, it repeats the process until it gets to the global environment which is the end of the look up.
Conclusion
Scopes is a very significant for the compiler to identify where to get the right variable at the right time, the models of scope Lexical and Dynamic scopes tailored down to which programming language is being used.
For JavaScript it's Lexical and that proves the point of Tokenizing as the first step in Compilation of our program, since Lexical scopes depends on where a function or block is created, when an variable/identifier is not found it goes a step outside it's current environment to look for that identifier until it gets to the Global scope.
Hope this Explains it!
Thanks for Sticking around!
Israel juri.