DEV Community

Cover image for Hoisting - One of the trickiest Javascript features
Coding Sam
Coding Sam

Posted on • Edited on

Hoisting - One of the trickiest Javascript features

Javascript is a programming language that can be tricky for beginners. Once you understand how it works, it becomes clear, but in the beginning, you end up debugging problems that you created yourself because you didn't know something about the language.
We all have been there.
Maybe you are learning Javascript and you are in this process of dealing with problems you don't see happening in most programming languages.

In this post, I will show you some code snippets and for some, an embedded codepen. However, you will see document.write calls instead of console.log. This is just to allow you to see the output on the codepen's app. Otherwise, you would have to open your browser's developer tools.

Let's look at the following JS code snippet:

console.log('Hello, my name is ', name);

var name = 'Sam';

What do you think that is going to happen?
Here, you are using a variable before declaring it and assigning a value. This kind of code, in most programming languages, would throw an error because you are trying to use something that is not known yet.
You will see the following output in the console:

Hello my name is undefined

There was not a single error, but you get an unexpected output.
undefined is the default value of any variable.

If you don't know what is going on, you start to ask yourself:

  • Why didn't the code crash when I tried to use an unknown variable?

  • Is there some magic that allows me to use a variable declared below? If so, why didn't I get the right variable's value and got undefined instead?

In the previous code example, try to replace name by something else:

console.log('Hello, my name is ', somethingElse);

var name = 'Sam';

You will get this:

ReferenceError: somethingElse is not defined

This error is something that any Javascript beginner would expect.
But it didn't happen in the previous example. It seems that you can use variables before they are declared.

Actually, something else happens behind the scenes to allow developers to do that. It's called Hoisting. Let me help you to understand one of the most tricky features of Javascript, specially for beginners. I will also show you how you can avoid it and when it can be useful.

What is Hoisting?

Hoisting is a process that moves all the declarations to the top of their scope. This is handled by the Javascript engine before running your code.

But... What is the scope of a declaration?

In Javascript, you have function based scope, which means, any variable declared inside a function, will belong to that function's scope, no matter where in the function it happens. For instance, if you declare a variable inside a for loop, that variable will be known anywhere in the function and not just in the loop.
But, any function or variable declared outside a function, will belong to the global scope.

Only declarations are hoisted (moved to the top).
But, which types of declarations?

  • variable declarations using the var keyword;

  • functions using the function keyword;

Be careful about var declarations. It is really easy to look at an assignment and think that the variable's name and value will be moved to the top.

Let's look at an example:

var a = 2;

In this line of code, there are two things going on, a declaration and an assignment. This line could be translated to:

var a;
a = 2;

Only the declaration var a will be hoisted. Like what happened in the first code snippet, if you try to use the variable a before the assignment, it will be undefined and not 2.

However, when you declare a function, you also write its body. You can't create a function with just a name and specify its behavior later on. That's why, when you declare a function (using the function keyword) the whole function gets hoisted. This means that, you can declare it in a line after the first call.
For instance, the following example will not throw an error:

var message = getMessage('Sam');
console.log(message);

function getMessage (name) {
  return 'Hello my name is ' + name;
}

You will see "Hello my name is Sam" on the console. The getMessage function got hoisted, which means, moved to the top, by the Javascript engine before running the code.

Let's see it in action!

Look at the following code snippet:


var message = getMessage('Sam');

function getMessage (name) {

  var completeMessage = intro + ' ' + name;
  var intro = 'Hello my name is';
  return completeMessage;
}

console.log('Message: ', message);

The output will be "Message: undefined Sam".
Before running the code, declarations will be hoisted. Let's break down Hoisting and follow through the steps:

  • First, collect all declarations (var and function)

Highlighting all declarations

  • Move those declarations to the top of their scope

Moving all declarations to the top of their scope

After this process, the Javascript engine will look at your code like this:

var message;

function getMessage (name) {
  var completeMessage;
  var intro;
  completeMessage = intro + ' ' + name;
  intro = 'Hello my name is';
  return completeMessage;
}

message = getMessage('Sam');

console.log('Message: ', message);

The message variable and getMessage function are global, that's why they were moved to the top of the file. The completeMessage and intro variables were moved to the top of the getMessage body, because they are attached to the function's scope.

The completeMessage variable will be undefined Sam, because the assignment to the variable intro happens one line below.

Can you run away from it?

How many programming languages do you know that have this Hoisting feature? Most programming languages don't have it, which means, if you try to use a variable or a function that is not declared yet, you will get an error. For me, it makes a lot of sense. Why would you use a variable before you declare it in the first place?
I don't really know why Javascript has Hoisting. But, I know that ES6 (ECMAScript version 6) added ways of declaring variables that are not hoisted.
ECMAScript is a standardized specification, that Javascript is based on.

If, at this point, you think that Hoisting was a bad idea, maybe you are not the only one and the people that work on the ES specification think like you. Maybe that's why they have created the const and let keywords, that allow you to define constants and variables, respectively. They are not hoisted.

Nowadays, a lot of Javascript developers are not using var anymore, because of Hoisting and the function based scope.
See my post on "Why don't we use var anymore?"

In the following example:

const message = getMessage('Sam');
function getMessage (name) {
  const completeMessage = intro + ' ' + name;
  const intro = 'Hello my name is';
  return completeMessage;
}

console.log('Message: ', message);

You will get an error inside the getMessage function, because you are trying to use the constant intro before you declare it and assign it a value.
Calling getMessage before defining it does not throw an error because, remember, functions are hoisted. But there is also a solution to create non hoisted ones. Just use arrow functions.

const getMessage = (name) => {
  return 'Hello my name is ' + name;
}

They are not hoisted because they don't have a name (you are not using the function keyword). When you create an arrow function, you are creating an anonymous function. If you want to keep a reference to it, you have to assign that anonymous function to a constant or a variable.

Is it all bad?

So far, I have shown you what Hoisting is and how you can avoid it. However, I think that it is not all that bad. There is one good use case for this feature.

Let's say you have a location.js file, that exports a function that any developer can use to get the user's location. Before getting the location, you need to ask permission to get that data. Your file would look something like this:

export const getUserLocation () {
  const userAllowedToGetLocation = askUserPermission();
  if (userAllowedToGetLocation) {
    return userLocation();
  }
  else {
    throw new Error('User refused to provide location');
  }
}

function askUserPermission () {
  // Logic to ask user permission to get his/her location
}

function userLocation () {
  // Logic to get the user's location
}

The exported function is a constant with an arrow function as its value. But, that function uses two other functions, which are defined after the exported getUserLocation.
You can do this because, remember, functions in Javascript are hoisted, but not arrow functions. If these auxiliary functions were arrow functions, you would have to put them before the exported one. This is a simple example but sometimes, you can have more than two auxiliary functions. Other developers that need to work on that file would have to scroll until they find what is being exported.

If you put all the auxiliary functions on the bottom of the file, you make it easier to read, because other developers will see the main logic as soon as they open the file, instead of having to scroll through a lot of functions.

I use this pattern a lot... But you might disagree ;)

Conclusion

Hoisting is a feature that can be tricky to a lot of developers. Fortunately, we have ways to avoid that process of moving all the declarations to the top of their scope. But, you can put this feature to good use. When you have a file that exports something, you can move all auxiliary functions to the bottom and make your code easier to read.

Do you like Hoisting? Do you think that it is useful? Do you know other ways of taking advantage of it? Let me know what you think in the comments!

Happy coding! :)

Top comments (0)