DEV Community

Cover image for Mastering JavaScript Function and Variable Hoisting: Best Practices and Examples.
Akindele Emmanuel
Akindele Emmanuel

Posted on

Mastering JavaScript Function and Variable Hoisting: Best Practices and Examples.

"Writing effective, error-free code requires an understanding of JavaScript's special function and variable hoisting behavior. This article delves into the details of hoisting, providing best practices and illustrative examples to enhance developers' proficiency. By mastering hoisting, developers can streamline their coding process and create more robust applications."

What is hoisting?

Hoisting is javaScripts default behaviour where variable and function declarations are moved to the top of their scope before the execution of code.

The JavaScript engine will normally execute code from the top to the button, line by line or one line after the other, and when it does, hoisting happens before execution during the semantic analysis phase. Variable and function declarations are moved to the top of their scope before the execution process. You can confirm this by using console.log() or document.write() to log out a function or variable before the code itself. Let us look at the illustrations below to shed light on the explanation above.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>   

     function square(a, b) {
        document.write(a * b);
     }
    square(3,4);

    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

This code will simply output 12 just as we can see below is the result of the code

an image showing the result of the code with function keyword square in the document when square is invoked
Now, you can also call or invoke the function before the function is created, and after it is created, it will still give the same result. Are you surprised? This is only possible because of hoising; now let's see that. You can also try this on your own.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        square(3,4); 

     function square(a, b) {
        document.write(a * b);
     }   

    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

This will still give the same result as above; it will output 12 in the document page when you use document.write() or in the console when you use console.log(), just like the example above.

the image showing the same reult or output when the function square is called or invoked before the function declaration.

This is possible because of hoisting. So hoisting moves declaration only and initialization or assignment are left in place. That means javascript initializations are not hoisted.

Types of Hoisting.

In this article we will be looking at two types of hoisting, which include:

  • Variable Hoisting
  • Function Hoisting
  • Function Expression Hoisting

What is variable hoisting?

This can simply refer to the behaviour of javaScript to move a variable declaration to the top of its scope, regardless of its actual position in the code.
Just as I have pointed out while explaining hoisting using a function as an example, variable declarations are also moved the same way the function is moved to the top, but in the case of variables, the output or result might seem a little bit different and not as direct as the case of function code that can be invoked or called before the actual code and it initializes. In the case of variable hoisting, different results for different types of variables. Let's check this out! In the example above, we were able to call or invoke a function before the actual code, and we saw the output: let's use the same method using any of the types of variables (let, const, and var). Let's see in the example below

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        let name = "abudog";
        const goat = "constano";
        var dog = "vanetino";
        console.log(name);
        console.log(goat);
        console.log(dog);
    </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

The code above worked without error, below is the result of the code.

the image shows the result of using variable key words such as let, const and var and the results in the console

But let's see what happens if we want to console.log() our variable declaration before the code or before we write the actual code. For example, let's say we have var, let, and const separated. Now let's see the different results that we will get.
Note: just write your code along for this will aid fast understanding of the subject matter.

<script>        
        //  we are looking at `let` variable keyword first
        // to see what will be log out in the console
        console.log(name);
        let name = "abudog";
        // console.log(dog);
        // var dog = "vanetino"; 
    //    console.log(goat);
    //    const goat = "constano"; 
    </script>

Enter fullscreen mode Exit fullscreen mode

Using the same principle as we have used in the code with function declaration, just like calling the variable before actual code. See the output below; you can as well try this on your own

the image of the result for using variable key word let, console.log() before the declaration of the code
The result is uncaught referenceError: cannot access ‘name’ before initialization
Let's also see for const variable what will happen

 <script>        
        //  We are looking at using `const` variable keyword now
        // to see what will be logged out in the console
        console.log(goat);       
        const goat = "constano";

        // console.log(name);
        // let name = "abudog";
        // console.log(dog);
        // var dog = "vanetino";
    </script>

Enter fullscreen mode Exit fullscreen mode

And this is the output from the console.

the image shows the rsult of the code called before the declaration using const variable keyword

Now we try var variable keyword and let's see the result

<script>        
        //  Now we are using `var` variable keyword
        // to see what will be logged out in the console
        console.log(dog);
        var dog = "vanetino"; 
    //    console.log(goat);
    //    const goat = "constano";
    //    let name = "abudog";
    </script>
Enter fullscreen mode Exit fullscreen mode

the image shows the result when hoisting when you use var variable key word

In the case of var variable keyword, the result is undefine.

Now we can see that the output is different from the first example, where a function keyword is used to declare the function. Variable hoisting is peculiar, and not only that, we saw that let and const variables output different results and var variable output undefined. Now let's see what happened. The let and const declaration is hoisted to the top of their scope, creating a variable name and goat with an uninitialized value. Let and const variables are not initialized until they are declared; they are in a temporary dead zone from the start of the scope until the declaration accessing a variable in the temporary dead zone, so this results in a referenceError. When you try to log name for let and goat for const variable, they are still in the TDZ, so javaScript throws a referenceError.

Why doesn't this happen with var?

Variables declared with var are also hoisted, but they are initialized with undefined immediately. This behaviour is by default for var variable before its declaration. Just like we can see in the last example above.
Don't forget the hoisting processes occur behind the scenes, and it gave birth to the results we can see or make the process work out the way we saw them.

Function and Function Expression Hoisting

What is function hoisting?

Function hoisting is a javaScript behaviour where function declarations are moved to the top of their scope, regardless of where they’re actually defined. This allows functions to be called before they’re defined. I refer to the first example at the beginning of this article.

<script> 
     function square(a, b) {
        document.write(a * b);
     }
    square(3,4);
    </script>
Enter fullscreen mode Exit fullscreen mode

Function declaration vs function expression

Before diving into function expression hoisting, let’s quickly review the difference between function declarations and function expressions:
Function declaration: a function declaration defined using the keyword, for example, function add( a, b) {
return a + b; }

Function expression: a function defined as an expression, often assigned to a variable; for example, const add = function(a, b) { return a + b; };

Here is the important thing or part: function expressions are not hoisted in the same way as function declarations. Only the variable declaration is hoisted, not the assignment. Consider the example below

<script>
        // this is a function declaration
         function add(a,b) {
            return a + b
         }

        //  this is a function expression
         const addConst = function(a,b) {
            return a + b;
         };

         console.log(add(3,5)  +  " " + "output of function");
         console.log(addConst(3,5)  +  " " + "output of function expression addConst");
    </script>

Enter fullscreen mode Exit fullscreen mode

There are two functions here: function declaration with the keyword function and function expression declared using the keyword const, these two should log out the same result. This is the result below

the image shows the result of function declaration with function keyword and a function expression

Now let's see what happens if we try to console.log() the two codes: the one with function declaration and the other with function expression. Using the same code.

<script>
        // This is a function
        console.log(add(3,5)  +  " " + "output of function");
         function add(a,b) {
            return a + b
         }

        //  This is a function expression
        console.log(addConst(3,5)  +  " " + "output of function expression addConst");
         const addConst = function(a,b) {
            return a + b;
         };         
    </script>
Enter fullscreen mode Exit fullscreen mode

Below is the output of the code

this image shows the result of function declaration and function expression when they are called before their codes

Now, the first code with function declaration worked very fine, and the second code with function expression using the const keyword to declare it displayed an uncaught ReferenceError: cannot access addConst’ before initialization. This is because only the variable declaration is hoisted in variables defined using const and let and not the assignment. So you can't call the function for function expression before it’s assigned to the variable. It is only possible when writing code with a function keyword.

Best practices for avoiding Hoisting issues

  • Declare variables at the top of their scope: Below are very good examples of declaring variable or function at the top of their scope before usage
<script>
        // Hoisting with function declaration
function salute() {
    console.log("Hello, Emmanue!");
}
// Hoisting with var
var name;
function meet() {
    // The variable `name` is hoisted, but it's undefined here until it is assigned a value
    console.log("Hello, " + name);
}
// Calling functions before they are defined
meet();         // Output: Hello, world!
salute();     // Output: Hello, undefined
// Assigning a value to the variable `name`
name = "Emmanuel";

// Calling the function again after assigning a value
meet();     // Output: Hello, Emmanuel
    </script>
Enter fullscreen mode Exit fullscreen mode
  • Use Let and Const for block scope: Always use let and const variable for block scope Example: in javaScript, let and const are used for block-scoped variable declarations, which means they are limited to the block in which they are defined either within a loop, condition, or a function In the code below we can see how to use this
<script>
        function blockScopeExample() {
    if (true) {
        let blockScopedVariable = "I am block scoped!";
        const blockScopedConstant = "I am a constant block scoped!";

        console.log(blockScopedVariable);  // Output: I am block scoped!
        console.log(blockScopedConstant);   // Output: I am a constant block scoped!
    }

    // Trying to access blockScopedVariable and blockScopedConstant here will result in a ReferenceError
    // console.log(blockScopedVariable); // Uncaught ReferenceError: blockScopedVariable is not defined
    // console.log(blockScopedConstant);  // Uncaught ReferenceError: blockScopedConstant is not defined
}

blockScopeExample();

// Example with a loop
for (let i = 0; i < 3; i++) {
    console.log(`Inside loop: ${i}`);  // Output: Inside loop: 0, Inside loop: 1, Inside loop: 2
}

// Trying to access `i` here will also result in a ReferenceError
// console.log(i); // Uncaught ReferenceError: i is not defined
    </script>
Enter fullscreen mode Exit fullscreen mode
  • Avoid re-defining variables in the same scope:
  • Minimize global variables:

Conclusion

Mastering JavaScript function and variable hoisting is crucial for any developer aiming to write clean, efficient, and error-free code. By understanding how hoisting works, particularly with var, let, and const, you can avoid common pitfalls related to variable scope and initialization.
Adopting best practices such as declaring variables at the top of their scope, using let and const for block scope, and favoring function declarations where appropriate will not only enhance the readability of your code but also prevent unexpected behaviors that can arise from hoisting.
As you continue to explore JavaScript, keep these principles in mind and apply them in your projects. With practice, you'll gain a deeper understanding of how hoisting impacts your code, enabling you to leverage this knowledge for more effective programming. Embrace these practices, and you'll be well on your way to becoming a proficient JavaScript developer, equipped to tackle complex challenges with confidence.

Top comments (0)