Understanding Hoisting
Some JavaScript concepts seem unusual, not to say strange. Today I would like to look at one of those mechanisms: hoisting.

From this article you will learn:
- What is hoisting?
- What are the differences between dynamic and lexical scope?
- What are lexicalization and tokenization in JS?
- Are let and const also affected by hoisting?
- What is the Temporal Dead Zone?
JavaScript is an interesting language. It has many concepts implemented in it that, for people encountering it for the first time, may seem strange, maybe even unintuitive. One of those mechanisms is hoisting.
Hoisting is a process that determines the scope in which a variable should be available. In most programming languages, a variable is available in the block where it was declared and, in addition, it can only be used after its declaration. In JavaScript, however, things work differently.
Before we move on to hoisting in detail, it is worth mentioning the scope of variables and functions.
Dynamic and Lexical Scope
Scope is nothing more than the area in which variables or expressions are available. In programming, we distinguish two kinds of scope: dynamic and lexical.
Dynamic scope is not the most popular concept in today's IT world. In fact, it does not appear in languages based on C, such as JS. It was originally introduced in Lisp, but we can also meet it in languages such as bash or LaTeX. In Perl, on the other hand, even though the language is based on lexical scope by default, we can choose dynamic scope.
What does the dynamic nature of scope mean? In dynamic scope, the program looks for the relevant block (loop, condition, function) where a given expression or variable appears, and then successively moves through the calling functions, using them to determine the value of that expression or variable. In other words, in dynamic scope the value changes depending on the order in which functions are called.
JavaScript is a language whose syntax is based on C. It is true that when Brendan Eich created JS, he drew from many different languages: C, Modula-2, and Java. The origin itself can already tell us that the concept of scope was introduced in a way similar to the languages used during its design. C, Java, and JavaScript use lexical scope.
Lexical scope (also called static scope) is based on the location of functions and variables at compile time. This means that the value of a variable will not change depending on subsequent function calls at runtime. Lexical scope is definitely easier for a programmer to manage. Very often it is enough to read a piece of code to infer a value without taking into account different call contexts that could affect it.
The example below shows how lexical scope works. The variable x depends only on the block in which it was declared. It does not matter where the function that returns the value of x was called. If JS had dynamic scope, the result of calling the bar function would be 2.
let x = 1;
const foo = () => {
return x;
}
const bar = () => {
let x = 2;
return foo();
}
bar(); // 1Hoisting
In JavaScript, we can declare variables in 3 ways, and functions in 2 ways. When it comes to declaring variables, we have var, let, and const at our disposal. In the case of functions, we can use the function keyword or an arrow function. At the beginning, let us focus on how hoisting works for the var and function keywords.
As I have already mentioned, hoisting is about determining the scope in which a given value should be available. We can safely say that this mechanism moves variable declarations to the beginning of their scope before the code is executed. This leads to a situation where variables are available even before their declaration. Let us look at the examples below:
foo();
function foo() {
console.log(a); // undefined
var a = 1;
}class Foo {
public static void main(String[ ] args) {
System.out.println(a); // error
int a = 1;
}
}The same function was written in two different languages. In both cases, the order of declarations and references to the variable is the same. The difference is that JavaScript (the first example) defines the variable as undefined (importantly, it does not return an error), while Java returns an error. Interestingly, the same mechanism also works for functions written with the function keyword.
To sum up, hoisting makes a variable available and allows it to be used even before it has been declared.
Lexical Scopes in JavaScript
It is worth mentioning that JavaScript has different kinds of lexical scope. Simplifying the topic, we can distinguish 3 basic ones: global scope (in the browser connected with the window object), function lexical scope, and block lexical scope. Block scopes were introduced to JS together with the new ways of declaring variables using let and const. We can say that function scope is a variation of block scope. The difference is that variables declared with var in other blocks (for example loops or conditions) are treated as belonging to the function block in which they were declared. Variables declared with let or const, however, belong to any block in which they were declared. Let us look at an example:
function foo() {
console.log(a); // undefined
console.log(b); // ReferenceError: b is not defined
if (true) {
var a = 1;
let b = 2;
}
}
foo();let and const vs Hoisting
There is a mistaken belief that the hoisting mechanism does not work for let and const. This comes from the fact that JS introduced an additional mechanism for checking references to variables declared this way. If we refer to a variable declared with let or const before its declaration, we get a very telling message: "Cannot access variable before initialization". If the hoisting mechanism did not work, we would get an error similar to the one we get when referring to a variable without declaring it. Let us see an example:
function foo() {
console.log(c); // c is not defined
console.log(b); // Cannot access 'b' before initialization
let b = 2;
}
foo();As we can see, we are dealing with two different situations. In the case of variable b, JavaScript knows about its existence, but says that we cannot use it before it is initialized.
The space between the availability of a variable and a reference to it is called the Temporal Dead Zone.
When Does Hoisting Happen?
JavaScript is an interpreted language. Interpreted languages use special programs, so-called interpreters, to analyze code. The code is analyzed line by line, and the machine code created in this process is not saved anywhere. Most often, interpreted languages are slower than compiled ones. Code is executed from memory as part of the whole process of interpreting code. Since we now know more or less what happens to JavaScript, then at what moment does hoisting happen?
First of all, we need to note that JavaScript is run in browsers in most cases. Modern browsers use JIT (Just-In-Time) compilation, which means that code is transformed into executable machine code and then run. JavaScript engines (such as V8 or SpiderMonkey) basically combine features of interpreters and compilers. JS code goes through two phases inside the JS engine. The first is the compilation phase, during which variable hoisting happens. In practice, this comes down to two processes: lexicalization and tokenization. In detail, it is nothing more than dividing code into smaller tokens, from which an AST (Abstract Syntax Tree) is then created for the analyzed scope.
At this moment, when the engine encounters a variable declaration, it allocates memory for it. It does not modify the code. This creates a binding between the variable and memory. While memory is being allocated for the variable, a default value is assigned to it, namely undefined. Then the scope is determined based on assignments, and only after that is machine code generated.
Summary
Hoisting as a phenomenon can be described in one sentence, but as we can see, there is a bit more to say about it. The term was coined by programmers to explain the whole process of memory allocation for variables during the compilation phase of JavaScript code more simply. As we can see, nothing happens without a reason - and the specific nature of this phenomenon is directly connected with how JavaScript works and where it is used.
Sources
- Lexical and Dynamic Scope
- Static and Dynamic Scoping
- What programming languages implement variable hoisting?
- Hoisting in JavaScript with let and const - and How it Differs from var
- Hoisting
- Javascript hoisting
- Javascript Phases and a bit of Hoisting
- The Secret of Hoisting in JavaScript
- JavaScript Hoisting
- Understanding Hoisting in JavaScript
- Co to jest hoisting w javascript?
- JavaScript Hoisting
- Hoisting
- Compiler vs Interpreter - Difference Between Them




Comments (1)
tworzeniestroninternetowych_warszawa_pl
31 sierpnia 2023 o 10:31Tekst wyjaśla hoisting w języku JavaScript w sposób przejrzysty. Mechanizm ten przenosi deklaracje zmiennych i funkcji na początek ich zasięgu, co może być mylące, szczególnie dla początkujących programistów. Wysuwa się istotne kontrasty pomiędzy zasięgami dynamicznym i leksykalnym. Hoisting wzbogaca zrozumienie tego, jak kod jest kompilowany i wykonywany w JavaScript. Pomimo że hoisting jest użytecznym mechanizmem, ważne jest, aby unikać niejednoznacznych deklaracji i korzystać z niego w sposób umiejętny.