Variable and Function Hoisting in JavaScript
Describing how variables and functions are hoisted under the hood
Introduction
Hoisting is a JavaScript term that describes a behaviour where variable and function declarations are moved to the top, enabling them to be usable anywhere irrespective of how deep down into the code they are declared. JavaScript hoisting is an interesting and yet tricky concept to understand. It has the power to be a lifesaver or the culprit behind the bugs that slow down your coding process.
Prerequisite
Basic JavaScript concepts, scoping in javascript
What is Hoisting?
The term "hoisting" refers to the act of "raising or lifting" into a higher position. This clarifies what the javascript hoisting mechanism accomplishes, which is to elevate or give precedence to all variable and function declarations within the Execution Context.
The Execution Context is an environment that is created when the JavaScript engine scans a script. It handles the execution of your code using two components.
First, is the Memory Component. This component searches through your code for any declarations of variables and functions, then creates memory blocks in which they are kept as pairs of keys and values. At this initial point, the values of variables are undefined. It doesn’t matter how deep into your code these declarations were made, now they exist at the top; they have been hoisted and can be used.
Next is the Code Component. In this component, code (variable assignments and references, function calls, conditionals, etc.) is executed line by line from top to bottom.
It is important to note that although a variable declaration is given precedence in the first phase of the Execution context, its initialization depends on the line-by-line execution of the script in the second phase. We will see why it is important to know this piece of information in later sections.
Consider the following examples to demonstrate these points:
console.log(a);
var a = “Afolabi”;
Explanation:
In this example, I used the variable a
by logging it into the console before declaring it. What do you think the result will be?
This means that although the variable has been declared it has not been assigned the value “Afolabi”. Instead, it was saved into memory with a value of undefined
In essence, the code has been translated into this under the hood:
var a;
console.log(a);
a = 'Afolabi';
How are we sure that this is how it works, and that a
exists? The result would be a ReferenceError if the variable has not been declared, that’s how we know.
console.log(a);
a= 'Afolabi';
Variable Hoisting
It is essential to understand that a variable's declaration style affects how it is hoisted. When compared to variables declared with the let and const keywords, var variables operate differently. Additionally, the scope in which a declaration is made affects how it must be used.
var, let, and const
Although these keywords perform essentially the same basic function, they are designed to be used in very different ways.
When a variable is defined using the var keyword in a function, it is viewed as though it was declared at the top of the function regardless of its actual position. The same is true when variables are declared using the var keyword outside a function. Such variables are moved to the top.
It is this behaviour that makes it possible to use variables declared with the var keyword before declaring them.
let and const, on the other hand, are translated differently. Variables declared using either of these keywords are not globally accessible. When variables are declared using either of them in a code block, within {}, they have to be declared at the top to avoid silly bugs when they are to be used.
For these reasons, only variables declared using the var keyword are hoistable. Variables declared using the let and const keywords cannot be used before they are declared.
console.log(myName);
var myName = 'Afolabi Joseph';
Output:
undefined
console.log(myName);
let myName = 'Afolabi Joseph';
console.log(myName);
const myName = 'Afolabi Joseph';
Output:
Uncaught ReferenceError: Cannot access 'myName' before initialization
It is also worth mentioning that for variables that are sure to be reassigned, use the let keyword. Otherwise always use the const keyword. This will help to avoid a lot of errors.
Function Hoisting
I have found using function hoisting advantageous when I need to invoke several functions in a particular order once a webpage loads. Although it may be argued that anonymous functions would accomplish the same task, I find that taking advantage of function hoisting results in cleaner and more elegant code.
A Function can be declared, either using a function declaration or a function expression. What is the difference?
A function declaration is translated directly as a function by the JavaScript engine. That is what it is. As we have stated earlier, all declarations are hoisted to the top of a script. So in the first stage of the Execution Context, a function declaration would already exist at the top of a script, ready to be invoked anywhere within the script.
printName();
function printName() {
let name = 'Afolabi Joseph';
console.log(name);
}
A function expression, on the other hand, is first translated to a variable whose value is undefined. This means that if we tried to access such a function, it would have been saved as a variable. Now that is a lot of trouble. Let's make some additions to the code above:
printName();
printAge();
function printName() {
let name = 'Afolabi Joseph';
console.log(name);
}
const printAge = () => {
let age = 1000;
console.log(age);
};
Conclusion
We have seen how JavaScript sees variables and functions when it evaluates a script and how important it is that we use methods that ease our programming.
In my opinion, the best practices for variable and function declaration that also consider hoisting are:
Variables should always be declared with the const keyword unless you are sure the value will be reassigned
Variable declarations should be done at the top level, both in a function and in the global scope
More explicitly, stick to const and let keywords
Use function declarations for all functions. But if you do prefer using a function expression, make sure to declare it before invoking such a function.
Write elegant and readable code.
Happy coding!