« Previous Tutorial Next Tutorial »

Let's ease back into things with a short, simple Quick Hit this week. We're going to talk about variable hoisting. In JavaScript, when you use the var keyword, whatever variable you create is "hoisted" to the top of the current scope. All that means is that the creation of the variable happens right at the start, even if you don't define it until further down in your code. The current scope might be the top level of your code, or it might be in a function, or a for loop, or similar.

Let's start with some data:

const cast = [
  'Daniel Craig',
  'Ana de Armas',
  'Chris Evans',
  'Jamie Lee Curtis',
  'Michael Shannon',
  'Don Johnson',
  'Toni Collette',
  'Lakeith Stanfield',
  'Christopher Plummer',
];

By the way, if you haven't seen Knives Out yet, you should. It's a terrific movie! Anyway, here's an example of hoisting:

function greetName(name) {
  console.log(greeting + ' ' + name);
  var greeting = 'Hello there,';
  console.log(greeting);
}
greetName(cast[0]); // 'undefined Daniel Craig Hello there,

So, you may notice the problem here. The creation of the variable is hoisted, but the value assignment is not. Basically the JavaScript engine will automatically convert our code above to this:

function greetName(name) {
  var greeting;
  console.log(greeting + ' ' + name);
  greeting = 'Hello there,';
  console.log(greeting);
}
greetName(cast[0]); // 'undefined Daniel Craig Hello there,

That's generally not super useful. I mean, I'm sure there are edge cases where hoisting is valuable, but for the most part the reason it happens has more to do with memory management on the JS engine's part than on anything in particular the coder is trying to do.

Because hoisting happens, however, we can't completely trust our JS code to run as expected in the case where we're expecting a variable not to be declared yet. Here's a function that specifically tries to break if a variable doesn't exist yet, but doesn't because of hoisting:

function listNames(names) {
  try {
    text
  } catch(e) {
    if (e.name === 'ReferenceError') {
      console.log('variable not declared yet!');
      return;
    }
  }
  var text = 'Starring: ';
  names.forEach(name => console.log(text + name));
}
listNames(cast); // all of our stars

This happens because hoisting causes text to exist, so it doesn't throw a reference error. We'd have to also check and see if text was undefined to be thorough.

This isn't a huge deal but it's best avoided entirely. There are two practices that help with that. The first is defining variables you know you're going to need later at the top of your scope. The second? Using let and const (though there is a growing school of thought that const is best avoided entirely … maybe we'll get into that at some point). let and const are still hoisted for memory management purposes, but they're not initialized, meaning they're not set to undefined. The JS engine knows they're there but will still throw a reference error if they're used before being declared in your code. Observe:

function greetNames(names) {
  names.forEach(name => console.log(greeting + ' ' + name));
  let greeting = 'Hiya, ';
}
greetNames(cast); // reference error

As you can see, we get a reference error here. Changing the let to a var would let the code run but greeting would be undefined at the time we're using it. Better, in my opinion, to stick with let and move it up to the top of the function, like this:

function greetNames(names) {
  let greeting = 'Hiya, ';
  names.forEach(name => console.log(greeting + ' ' + name));
}
greetNames(cast); // it works!

So, there you go. Variable hoisting … it's a thing. Understanding how it works is valuable, even if only so you can appreciate why the best practice of declaring your variables at the top of their scope is, well, a best practice!

See you next week.

As always, you can download example files for each of these tutorials from the JS Quick Hits github repo.

Enjoying these quick hits? You can get them five days early by subscribing to our weekly newsletter.

« Previous Tutorial Next Tutorial »