9 questions on hoisting in JavaScript answered
Learn from questions commonly asked on the web regarding hoisting in JavaScript and clear all your doubts.
Hoisting is perhaps one of the most peculiar aspects of JavaScript. It's something which doesn't get help from experience of another programming language — as far as I know, almost no other programming language exhibits hoisting like JavaScript does. In fact, to some extent, even the term "hoisting" is perhaps nascent for a programming language.
Due to its nature, hoisting is either misunderstood completely or understood without crystal-clear clarity. In this respect, there are numerous questions people tend to ask with regards to hoisting and that why is it even something to worry about in today's date.
This article hopefully aims to clear all your confusions regarding hoisting in JavaScript. By the end of it — and I encourage you to read till the end — you'll be absolutely confident in your ability to reason about the whole intuition behind hoisting and when you might leverage it, if at all.
Why does JavaScript have hoisting?
This question can be broken down into two questions:
- Why did JavaScript have hoisting in its early days?
- Why does JavaScript have hoisting to date?
Let's answer each one.
Why did JavaScript have hoisting in its early days?
Remember that JavaScript had extreme simplicity as a design goal. The idea was to appeal to non-technical people so they could easily add simple interactions in their websites. And to a large extent, this goal was indeed met.
Many features were added to the language exactly for this very reason — simplicity. Hoisting is one such example.
The initial motivation in JavaScript was function hoisting, whereby a function is accessible even before its definition. Variable hoisting, as per Brendan Eich's own words, was an unintended consequence of function hoisting.
So the reason why JavaScript had hoisting in its early days was to make the language simpler. Without a doubt, it would've been superbly convenient for people to call functions without having to worry about whether they were even declared before that or not.
Why does JavaScript have hoisting to date?
This is answered later below.
How does hoisting work in JavaScript?
We all have heard of the fact that hoisting works by taking the declaration at the top of its scope. In reality, this is true but with some abstraction applied to the explanation.
Let's dive a little bit deeper into how hoisting actually works...
First the code is parsed and then the parsed version goes through a first pass. In this first pass, all variable and function declarations are found and taken to the very start of their scope. In other words, in this first pass, the interpreter basically hoists all declarations. In the second pass, the code finally gets executed.
Is this more work for the interpreter? Definitely yes.
Does it affect performance negatively? Not much, if at all. The thing is that hoisting is done once — in the first pass after parsing — so there's no continual overhead as a script runs. Furthermore, the overhead of hoisting in the first pass is also almost negligible.
Explain hoisting with an example
Actually there are many classifications of hoisting. So this question boils down to showing an example for every classification.
var hoisting
Let's review var hoisting to begin with:
// This doesn't throw because of `var` hoisting
console.log(x); // undefined
var x = 10;let and const hoisting
Now, let's see let hoisting (const works similarly):
// This throws due to the TDZ
console.log(x);
let x = 10;Uncaught ReferenceError: Cannot access 'x' before initialization
Before where x is declared, we try to access it. This immediately throws an error because the region before a let declaration, in its respective scope, is a temporal dead zone where the variable's access fails.
Keep in mind that because knowledge of the fact is kept that a let declaration appears later when we access x in console.log(x) above, there is surely some kind of pre-processing involved here before executing the code.
let, leaving the term "hoisting" to only refer to a case where it's allowed to refer to an identifier before its declaration.Function hoisting
Anyways, let's now see function hoisting:
// The function is accessible even before its definition
greet(); // 'Hello world!'
function greet() {
console.log('Hello world!');
}By far, the only useful aspect of hoisting lies in this type.
class hoisting
Next up, we have class hoisting, which is more or less just like let/const hoisting:
console.log(Foo);
class Foo {}Uncaught ReferenceError: Cannot access 'Foo' before initialization
In the code above, we try accessing the Foo class before its declaration. The code throws an error because of the temporal dead zone in which Foo is prior to its declaration.
class — it's invalid to refer to the class prior to its declaration, likewise if our definition of "hoisting" only allows for cases where it's valid to access an identifier before its declaration, then this certainly doesn't apply.import hoisting
And then finally there's import hoisting as well but that's quite apparent because JavaScript clearly states that all import statements are processed at the start of evaluating a given script:
export const a = 10;// This works because of `import` hoisting
console.log(a); // 10
import { a } from './constants.js';In the code above, the console.log(a) statement logs the value of a, which is 10, without any issues even though the import declaration occurs after it. This is by virtue of import hoisting.
Why do we need hoisting in JavaScript?
This question can also be rephrased as: What is the application of hoisting in JavaScript?
Well, there isn't anything for which we explicitly require hoisting in JavaScript. That is, hoisting isn't something which we need for our JavaScript applications; it's just a casual behavior exhibited as per the language's design.
To better understand what this means, let's make a quick comparison.
Consider the default-valued parameter feature in JavaScript. Why do we need a default-valued parameter? Well, it's when we need to define a function with a parameter that isn't required and, in case of not being provided with a value, assumes a default value.
You see, there is a clear-cut application of the default-valued parameter feature in JavaScript. However, there is nothing like this for hoisting. Hoisting is, more or less, not a utilitarian feature but rather a behavioral feature; it only makes up the behavior of JavaScript when executed.
And that's why there isn't any real application of hoisting when we're working with JavaScript.
Why is hoisting still supported in JavaScript if it generally confuses code?
Today, in JavaScript, the biggest reason why hoisting is still supported is for backwards compatibility. Legacy scripts might rely heavily upon hoisting and so removing it completely would mean bringing these scripts to a halt in new browsers.
Besides that, hoisting is also supported for convenience sake. It won't be completely wrong to say that function hoisting is particularly useful at times (though, it isn't something that I think other developers should eagerly aim for or that new languages should copy in any way).
What is the difference between function hoisting and variable hoisting?
Function hoisting refers to the behavior whereby a function is made available at the start of its respective scope before its declaration.
In contrast, variable hoisting refers to the behavior whereby a variable's declaration is made available at the start of its respective scope, for var, or where the variable is entered into a temporal dead zone at the start of its respective scope prior to its declaration, for let (and const too).
var hoisting, or let hoisting (which might not classify as hoisting depending on how the term "hoisting" is defined).Function hoisting was a real feature of the language (in its very early days); var hoisting was an afterthought of function hoisting; let hoisting was purely for the sake of remedying the flaws of var hoisting.
Can we avoid hoisting in JavaScript?
Being an intrinsic language feature, we can NOT technically avoid hoisting in JavaScript, so to speak.
However, we can deliberately structure our code so as to not leverage the convenience of hoisting (it's only a slight convenience for function hoisting but a troublesome trait for other kinds).
What are the advantages of hoisting?
Function hoisting is the only kind of hoisting in JavaScript which I think could be considered as being useful in some cases.
I am personally not a really big fan of function hoisting either but sometimes it might prove handy to have all the functions come last in a script after all concrete logic and implementation details have come.
But apart from function hoisting, there isn't much benefit of hoisting, if at all.
Do let and const get hoisted?
Again, this depends on the way we define the term "hoisted" here. If we take it to mean that an identifier is accessible before its declaration site without any errors being generated, then let/const do not get hoisted.
But if we take the term to mean that an identifier goes through some pre-processing for its declaration before the code is finally executed, then yes let/const do get hoisted. In this latter definition, let/const enter a temporal dead zone before their declaration site.