You should read the previous blog first to get better insights before reading this.
But to recap: In JavaScript, an execution context is a concept that deals with the idea of scope or the environment in which code is evaluated and executed. It's the realm in which variables, functions, and objects are accessible. When JavaScript code runs, it does so within an execution context. Each execution context has three main components:
Variable Environment: This component consists of the variable and function declarations within the context. It is closely related to the concept of hoisting, as during the creation phase of an execution context, variable and function declarations are moved to the top of their respective environments.
Scope Chain: This component provides ordered access to variables based on their hierarchical nesting on the code. It consists of the variable object of the current context and the variable object of all its parents (lexical environment), forming a chain-like structure. The engine looks up the scope chain when trying to resolve identifiers (like variable names).
this
Binding: This component refers to the value of this
in the context. Depending on how a function is called, this
can refer to different objects.
this
refers to the global object (like 'window' in a browser).this
depends on how the function is invoked - if it's called as a method of an object, this
is the object;this
is the global object (or undefined in strict mode).this
is lexically bound, meaning it's set to the this
value of the outer function where the arrow function is defined.The JavaScript Variable Environment is the area where variables are stored during the execution of a JavaScript program.
Each function in JavaScript has its own variable environment. When a function is invoked, a new execution context is created, and this new context will have its own variable environment, which is separate from other contexts.
The variable environment includes:
Now as we understand variable environment we can explain hoisting.
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their containing scope during the CREATION phase, before the code has been executed.
Variable Hoisting:
In JavaScript, a variable can be declared after it has been used. In other words, a variable can be used before it has been declared. This is possible because JavaScript engines hoist variable declarations to the top of the script or function.
For example, the following code will work in JavaScript:
Even though myVar
is declared after it's logged to the console, JavaScript doesn't throw an error. Instead, it hoists the variable declaration var myVar;
to the top of the scope, so the actual order of execution looks more like this:
This is why myVar
is undefined
in the first log. The variable has been declared but not yet initialized with a value.
One thing to note is that only the declarations are hoisted, not the initializations. If a variable is both declared and initialized at the same time as in var myVar = 5;
, only the declaration var myVar;
will be hoisted, not the initialization myVar = 5;.
Hoisting with let and const:
With ES2015 (let and const) hoisting works a bit differently. While declarations are still hoisted, the "Temporal Dead Zone" (TDZ) - a period from block start until declaration where they cannot be accessed - prevents their use until after their declaration has been initialized. This is why using a let or const variable before it's declared results in a ReferenceError
.
In the above example, myLetVar
is still hoisted to the top of the scope, but it's not initialized with a value of undefined
like var variables are.
This means you'll get a ReferenceError
if you try to use it before its declaration.
Function declaration Hoisting:
Just like variables, function declarations are hoisted to the top of their current scope. But unlike variables, function declarations are hoisted with their body.
In the above example, even though the function myFunc
is invoked before it is declared, the JavaScript engine doesn't throw an error. Instead, it hoists the entire function declaration to the top of the scope, so the actual order of execution is like this:
Function expressions and arrow function Hoisting:
Unlike function declarations, function expressions in JavaScript are not hoisted. This is because function expressions involve assigning an anonymous function (or a function with a name) to a variable, and the variable assignment is not hoisted, only the variable declaration is. Let's illustrate this with an example:
In the above code, when we try to log myFunc
before its definition, it returns undefined
rather than the function or an error. This is because the variable myFunc
is hoisted (i.e. the JavaScript engine is aware of its declaration), but its assignment as a function happens only where you've written it in the code, which is after the log statement.
The same principle applies when using let
or const
:
Here, because of the Temporal Dead Zone associated with let
and const
, trying to access myFunc
before its definition results in a ReferenceError
.
Arrow functions, like function expressions, are not hoisted. This is because arrow functions are assigned to variables, and while variable declarations (let, const, var) are hoisted, their assignments are not.
You get a TypeError because at the point where myFunc
is invoked, myFunc
is undefined due to hoisting, and trying to invoke (call with ()) undefined throws a TypeError
.
Understanding hoisting can help you avoid potential bugs and errors in your code. It's generally considered good practice to declare and initialize your variables at the top of their scope to make it clear that they're available for use throughout that scope.
Share:
Accelerating Digital Success. Experience the future of web development – faster, smarter, better. Lets innovate together.
©2024 Dreit Technologies | All rights reserved