Hoisting remains one of the quirkier aspects of JavaScript. When a developer declares a variable in JavaScript, that variable behaves as if it’s been lifted to the top of it’s available scope. This article isn’t going to concern itself with what actually happens behind the scenes to cause this behavior. Instead, I hope to provide a simple explanation of hoisting that will make sense to new users of JavaScript.
I went about 1.5 years without really understanding hoisting. I just stuck to a general rule — I always declared and assigned all of my variables at the top of the file. Before ES6 came out, that looked something like the code below:
var a = 1;var b = 2;var c = 3;
var adder = function (a, b, c) {return (a + b + c);};
adder(a, b, c); // Returns 6
The above code executes correctly and returns the number 6 when I run it. I declare and assign my variables at the top, then I define a function expression, and then I call my function, passing in the variables I’ve assigned earlier on.
But I get the same return value when I declare my variables at the top without assigning them, like this:
var a;var b;var c;
var adder = function (a, b, c) {console.log(a + b + c);};
a = 1;b = 2;c = 3;
adder(a, b, c); // Returns 6
Or I can call my function before I declare it, and I’ll still get 6 back:
var a = 1;var b = 2;var c = 3;
adder(a, b, c); // Returns 6
function adder(a, b, c) {console.log(a + b + c);};
Here’s a challenge. Before you keep reading, consider the code below and try to determine what it will evaluate to.
function hoist() { a = 1; var b = 2;}hoist();console.log(a); console.log(b);
Got it? OK, I’ll share the answer with you.
console.log(a)
will return 1
. On the other hand, console.log(b)
will come back with `ReferenceError: b is not defined`
. Both a
and b
are hoisted to the top of their scope, but because b
was declared inside of the hoist()
function, it’s not available to the global scope.
let
and const
introduce a different way of handling hoisting.
We’ll start with let
. In the example below, I’m trying to log the hoist
variable, and then declaring and assigning it:
console.log(hoist); // ReferenceError: hoist is not definedlet hoist = 'hoisted';
In this case, I got a ReferenceError
. This is different from what I would have gotten had I written var hoist = 'hoisted';
. In that case, I would have ended up with undefined
.
var
and let
throw different errors because JavaScript initializes them differently. When var
is declared, JavaScript sets it to undefined
. let
, on the other hand, remains uninitialized. That way, it throws a helpful error rather than just returning undefined
.
Let’s try this with const
:
console.log(hoist); // ReferenceError: hoist is not definedconst hoist = 'hoisted';
Like let
, we get an explicit error telling us that hoist
was not defined before it was called.
JavaScript will hoist function declarations, but not function expressions. I’ll provide some examples:
If a function is declared but isn’t assigned to a variable, it will be hoisted to the top of it’s scope.
hoister(); // hoists
function hoister() {console.log('hoists');};
A function expression, on the other hand, will not be hoisted, so the below code returns a TypeError
.
hoister(); // TypeError: hoister is not a function
var hoister = function () {console.log('hoists');};
Hopefully this post has helped you to make a bit more sense of hoisting. Of course, there’s always more to learn.
Let me know in the comments below if you have any questions, and please clap if you enjoyed this post!