Hoisting: Why VAR is a no-go

Nicole Gooden
4 min readFeb 26, 2021
JavaScript Hoisting text with upside down hook on left side

So, you can write JavaScript code that executes, but does it execute as expected?

Do you ever wonder what’s going on behind the IDE? Do you know how your code really works?

Let’s consider this code snippet; hope you like dogs!

(Don’t worry, I won’t be using var for long.)

var dogs = [
{ name: 'Golden Retriever', age: 6, loved: true },
{ name: 'Bernese Mountain Dog', age: 2, loved: true },
{ name: 'Lab', age: 5, loved: true },
{ name: 'Dalmatian', age: 3, loved: true }
];
function sortByKey(dogs, key) {
return dogs.sort((dogA, dogB) => {
return dogA[key] - dogB[key];
});
}
var alphabetizedDogs = sortByKey(dogs, key);var key = 'age';console.log(alphabetizedDogs);

Imagine you are JavaScript (imagine having so much power, yet so little structure)…

How would you interpret this code?

The Creation Phase — JS takes a look at the code to determine which variables and functions need to be stored in memory before truly digging into the execution of the code. During this phase, something called hoisting takes place. Essentially, JS rearranges the code in a particular way. JS is a single-threaded language, meaning it interprets code one line at a time. The rearrangement usually follows this pattern, with declarations at the top (function, let, const, var, class, etc.):

//From top to bottom: var dogs, alphabetizedDogs, key = undefined;         //declarations


function sortByKey(dogs, key) { //function definitions
return dogs.sort((dogA, dogB) => {
return dogA[key] - dogB[key];
});
}
dogs = [ //variable assignment
{ name: 'Golden Retriever', age: 6, loved: true },
{ name: 'Bernese Mountain Dog', age: 2, loved: true },
{ name: 'Lab', age: 5, loved: true },
{ name: 'Dalmatian', age: 3, loved: true }
];
alphabetizedDogs = sortByKey(dogs, key);key = 'age';

console.log(alphabetizedDogs);

At the very top, JS identifies the variable declarations. You might be wondering about the values of these variables. What are they?

Well, initially, they are all undefined but as JS works through each line of code in this order, each variable is assigned the intended value…except key doesn’t get its value in time.

A glimpse into the JS mind:

Oh awesome, I have three variables right now, but they haven’t been assigned values yet. I’ll go ahead and make them all undefined until I recognize the assignment = operator. Great, and I have a function, too. Okay…so now dogs is an array, alphabetizedDogs is assigned the function I saved earlier, and…oh yeah, it looks like I have to invoke the sortByKey function and assign its return value to alphabetizedDogs . Okay, got it! Let me go find that function and execute, then I’ll pick up where I left off.

Alright, now that I’ve executed that function and assigned a value to alphabetizedDogs , I can continue by assigning key a value of 'age' . Great, let’s log alphabetizedDogs to the console! It’s another job well done today.

Here is my output. Is this what you expected to see?

[
{ name: 'Golden Retriever', age: 6, loved: true },
{ name: 'Bernese Mountain Dog', age: 2, loved: true },
{ name: 'Lab', age: 5, loved: true },
{ name: 'Dalmatian', age: 3, loved: true }
]

Given the fact that I’m sorting by age (youngest to oldest), I expected this output:

[
{ name: 'Bernese Mountain Dog', age: 2, loved: true },
{ name: 'Dalmatian', age: 3, loved: true },
{ name: 'Lab', age: 5, loved: true },
{ name: 'Golden Retriever', age: 6, loved: true }
]

Okay…what went wrong?

Well, at the time I executed the function sortByType() , the argument passed for the parameter of key was undefined . It only had the value I gave it because I hadn’t reached its assignment yet. Since undefined isn’t a key on any of the array elements, there wasn’t any criteria by which to sort. I did the best I could (not sorry).

How can I make JS log the expected output?

I have a few options.

  1. Move the key assignment toward the top of my code, prior to the invocation of sortByType .
  2. Ditch var — start using let and const from ES6!

Let’s try both.

Consider how this code changes when var is replaced by let.

let dogs = [
{ name: 'Golden Retriever', age: 6, loved: true },
{ name: 'Bernese Mountain Dog', age: 2, loved: true },
{ name: 'Lab', age: 5, loved: true },
{ name: 'Dalmatian', age: 3, loved: true }
];
function sortByKey(dogs, key) {
return dogs.sort((dogA, dogB) => {
return dogA[key] - dogB[key];
});
}
let alphabetizedDogs = sortByKey(dogs, key);let key = 'age';console.log(alphabetizedDogs);

Now, when I run this code snippet, I see this error:

Cannot access key before initialization.

Not only will JS refuse to finish its execution, but it also provides you with this helpful error message.

Wait, hold on! I want to invoke sortByKey with two arguments, one is the dogs array I already know about, and the other is key …what’s key ???

Hoisting still occurs for these variables, but instead of receiving an undefined value from the start, the const and let declarations are not initialized, which ultimately catches the bug before it’s able to cause any problems!

let dogs = [
{ name: 'Golden Retriever', age: 6, loved: true },
{ name: 'Bernese Mountain Dog', age: 2, loved: true },
{ name: 'Lab', age: 5, loved: true },
{ name: 'Dalmatian', age: 3, loved: true }
];
function sortByKey(dogs, key) {
return dogs.sort((dogA, dogB) => {
return dogA[key] - dogB[key];
});
}
let key = 'age';let alphabetizedDogs = sortByKey(dogs, key);console.log(alphabetizedDogs);

Perfect! As long as key is assigned its value prior to being referenced, we’re all set. Var comes with tons of bug-prone deficits; one of which is that undefined variables can make their way through code execution.

In my next article, I’ll be diving into let and const more deeply. I will also introduce the concept of scope: global, local, and block. Stay tuned!

--

--

Nicole Gooden

I'm Nicole, a software engineer and alum of Turing School of Software and Design's Front-End Engineering Program. Hope someone out there enjoys my content!