From Top to Bottom. Hoisting in JavaScript

Emil Andreasyan
The Startup
Published in
5 min readSep 12, 2020
Mountain Ararat

In this article, we’ll go through concept of hoisting, it’s definition and implementation.

Hoisting represents a system or mechanism by which variables and functions are moved to the top of the scope before the code is being executed.

That means, that we have access to the variables and/or function no matter where in scope they are written. Consider the following example

console.log(name)
var name = "Adam" // "undefined"

Here, in the first line, we try to console log the name variable, which is declared only on the second line. So, by an idea, hoisting will mean that this var name = “Adam” is taken to the top of the context before console.log(name) even runs. In that case, why does this technique prompt us with undefined? If JavaScript doesn’t see this code, it would give us ReferenceError and, vice versa, if it sees this variable, the value of the name variable would be returned as a string Adam. But we get neither error nor the value, instead, we get something in the middle (if we’re allowed to say so), the undefined. What’s the issue? To uncover this, first, we need to step back and remember what variables contain. In the example above, we not only declared a variable but also assigned it to some value, thus, we can say, that the variable above is declaration + assignment (to familiarize yourself a bit more with this topic, you can check out this post). JavaScript allows us to both declare and assign variables in the same line, but in its invisible world, it takes these two steps separately. As you might know, if we only declare a variable without assigning it, it will be undefined while typeof variable // “undefined” (we’re getting to the point). That means that hoisting takes to the top of the scope only declaration, without assignment! The example below makes it crystal clear how the Hoisting works:

var name; // "undefined"
console.log(name)
name = "Adam"

So, we might say, that Hoisting doesn’t take the whole variable at the top, but parses (or, rather, references) its declaration only. Imagine if you attend a concert with your friend, but for some reason the security guard only lets your friend in, separating and leaving you outside, explaining that even you’re always together like twin brothers, only one of you has access… That’s a bit weird.

Another thing to mention about Hoisting is that the place when the variables and functions are hoisted is different related to whether we use a global scope or function scope (simply, the area between curly braces, {…}). The function below fully demonstrates the hoisting concept in functions.

function personalInfo() {
var firstName = 'Adam';
var lastName;
console.log(firstName);
console.log(lastName);
console.log(address);
console.log(mobile);
console.log(dateOfBirth);
lastName = 'Smith';
var address = 'planet earth';
mobile = 1234567890;
};
personalInfo();// Adam
// undefined
// undefined
// ReferenceError: mobile is not defined
// ReferenceError: dateOfBirth is not defined

In the first line in function scope which reads var firstName = ‘Adam’, doesn’t require hoisting while it was both declared and assigned at the top of the scope, before being invoked (console.log(firstName)). The lastName variable was declared before invocation but assigned after it, that’s why it’s undefined (this is the best illustration of hoisting). The address variable is both declared and assigned after being called, so the JavaScript only takes declaration at the top and again prompts us with undefined. Another interesting example is when we call a variable that was nor declared neither assigned (dateOfBirth). And the last example of the mobile variable will give us an Uncaught ReferenceError, while when we declare a variable without explicitly putting var, let, or const keywords before the name, it becomes a global variable, which is not visible in the function’s scope if put after calling, but is accessible once we move it outside of function scope into the global scope. Even if we put this global mobile variable after function but before calling it, it will be visible.

function personalInfo() {
console.log(mobile);
};
mobile = 1234567890; // outside of function scope, in global scope
personalInfo();
// 1234567890

Regardless of that, it was called before it was declared, as far as the variable is also hoisted globally, it can now be invoked.

With the introduction of ECMAScript 2015, the let keyword is now thoroughly substituting var-s, which has scope issues. Now when we declare a variable by let, it won’t return undefined, but ReferenceError instead.

function differenceBetweenVarLetConst() {
console.log(firstName);
console.log(lastName);
console.log(address);
var firstName = 'Adam';
let lastName = 'Smith';
const address= 'planet earth';
}
differenceBetweenVarLetConst();
// undefined;
// Uncaught ReferenceError: can't access lexical declaration 'lastName' before initialization;
// Uncaught ReferenceError: can't access lexical declaration 'address' before initialization;

So, let is more specific, and not only in case of var-related scope issues but also by explicitly complaining in form of error that it was initiated after it was called. Both let and const is uninitialized when tried to be hoisted, whereas var remains undefined.

Function declaration vs function expression

The last difference in hoisting behavior is vividly underlined when comparing function declaration vs function expression. Which one of them is hoisted? Function expression, unlike function declaration, is a function stored in a variable. Consider the following examples.

// function declaration
declaration(); // called before initialization, hoisted
function declaration() {
console.log('function declarations are hoisted')
}
// function declarations are hoisted
// function expression
expression(); // called before initialization, not hoisted
let expression = function() {
console.log('you will not see this message while function exressions are not hoisted')
};
// Uncaught ReferenceError: can't access lexical declaration 'expression' before initialization;

It becomes obvious that function declarations are seamlessly hoisted compared to that of function expressions, so, keep that in mind next time you decide to write a function!

Conclusion

The takeaways of this article are that the JavaScript engine takes variable declarations to the top of its environment, which can be both global and local scopes. It can be quite informative depending on whether it returns undefined or ReferenceError. In case of undefined, it says that the variable is created, it exists, but it’s assigned after the invocation and it’s declared as var (no matter if it was declared before). In the case of ReferenceError, JavaScript points out that the variable we try to call is not visible while it was neither declared nor assigned, or it’s declared+assigned with let or const. Also remember, that only function declarations are hoisted. To avoid these issues, always declare and assign variables at the top, before calling them!

--

--