Understanding JavaScript Under the Hood
It’s not a secret that understanding how things really work makes you a better developer and a better coder. My purpose here is to help you understand JavaScript and some concepts that we as developers constantly overlook—and let’s be honest, this is one of the reasons why JavaScript is one of the most misunderstood programming languages, despite its popularity.
So without further ado, let’s start understanding the “whys” and “hows” of JavaScript.
The first thing you need to know about this language is that all the code you write gets executed within an environment called the “execution context.” Let’s dig into this, because it is fundamental to understanding JavaScript at a deeper level.
The Global Object and “this”
Before we dive into what the execution context is, it is important to first understand two elements that are generated by the JavaScript engine: the “global object” and the special variable called “this,” which are always available to us even without a single line of code.
For example, let’s say you’ve defined the following object, and you want to access it:
One way to do this is by simply calling the object by its name (pet), another way is by calling it using the global object (window.pet), and last but not least by using the keyword “this.”
At this point, you might be wondering why we can access the object “pet” through the global object window. To answer this, we need to go one level down and understand how the execution context gets created.
Execution Context Creation
The first thing the JavaScript engine creates before it starts to execute code is the “global execution context.” Once this gets created, a parser scans every single line of your code, looking for variables and functions. Basically, it takes a look at the things you want to use and sets aside memory space for each of them, allowing you to invoke functions and variables prior to their declaration, like this:
Something important to mention here is that there is a difference between functions and variables in this creation phase. Functions are placed entirely in memory space (i.e. both their name and execution code), whereas variables only have their name stored in memory. By default, the engine sets the value of all variables to undefined.
Quick tip here: never set a variable equal to undefined (i.e. var myVariable = undefined). Even though JavaScript lets you do that, it’s better to not use undefined as a variable initializer since this special keyword is used by the engine to let the developer know if a variable has never been set. Following this tip will save you tons of headaches when debugging code (true story!).
Another important part of understanding JavaScript is understanding the relationship between the execution context and the way functions work, which is what the next topic is all about.
Functions
Functions have a particular behavior in regard to the execution context. For example, let’s say we have the following script:
As I previously mentioned, the first thing that always gets created is the “global execution context.” Something important you need to know about this is that, even though the parser scans your code line by line in the creation phase and sets up memory space for your functions and variables, none of the functions get executed at this point. The only way JavaScript executes functions is when you invoke them using “functionName()”.
Right after the creation phase, the execution phase is triggered. In this phase, each function invocation creates a new execution context, and each context is placed one on top of the other in what’s called the “execution stack.” Whichever one is on top is the one that’s currently running.
Each new context will have its own space for variables and functions, which means it will go through the creation phase and then execute the code line by line within the function. If it happens to find another function invocation, the execution of the current function stops, and another execution context gets created on top of that, starting a new cycle.
When the function on top of the stack finishes, it gets “popped off” the stack; then, the next function gets executed, and the process is repeated until it gets to the global execution context at the bottom of the stack.
Now that we know that each function has its own execution context, it’s time to explain a little bit how variables behave in different contexts.
Execution Context and Variable Environment
Basically, a variable environment is where the variables live and how they relate to each other in memory.
Let’s analyze the following code and its output:
As you already know, the first thing that gets created is the “global execution context,” and all functions and variables like the one declared in line 12 are put into memory. Then, the execution phase starts, and the variable declared in line 12 gets a value of “1.”
When the execution hits the invocation of the “function first()”, what do you think is going to happen? That’s right: a new execution context gets created for that function, and the variable declared in line 7 will be put into this new context variable environment.
So, what I want to make clear is that, even though the variables declared have the same name, they are unique in memory because each of them was defined within different execution contexts. That’s why the output looks like it does.
I hope this post has helped you to understand at least one of the many unique and interesting parts of JavaScript. There is just one last thing I want you to take with you after reading this: be curious, learn how things work under the hood, question everything, and trust me—you will stand out in this field.