Hoist Your Vars! (Variable Hoisting in JavaScript)
Tags: javascript, codenewbie, beginners
Published: 2020-02-20 02:56:24

At its core, hoisting is an "order of operations" issue. JavaScript code goes through two phases: compilation and execution. 

- Declarations (`var`, `let`, `const`, and `function`) are read *first* during code compiling.
- Assignments (`thing = value`) and function calls (`someFunction()`) are read *second* during execution.

The main difference between the `var`, `let`, and `const` declarations is the way they are/can be **initialized**. `var` and `let` can be initialized without a variable, or without *pointing to any value*. Attempting to initialize `const` without a value will throw a reference error. 

You can declare `var` and `let` variables anywhere in your code, and then assign them anywhere else. With `const` you must declare and assign a value at the same time. 

During the compiling phase, variable declarations are ***hoisted*** to the top of the code, below `function` declarations, and above everything else. 

Some example code: 

```js
    console.log(thisVar)
    var thisVar = "Hoisted" 
    
    // compiles to: 
    var thisVar
    console.log(thisVar)
    thisVar = "Hoisted"
```

If you were to try to run this piece of code, this would be your result: 
```js
    console.log(thisVar)
    var thisVar = "Hoisted"
    
    //OUTPUT: 
    > undefined
```
The `var thisVar` declaration is read, but the assignment comes after the function call (or `console.log()`, in this case), which causes the result to be `undefined`, because the program knows that the variable exists, but at the time of the `console.log()` does not yet know what value it points to. 

Another significant part of hoisting is the ability to call a `function` before it has been declared in your code. 

As mentioned earlier, both `var` variables and `function` declarations are read first during compiling. Function calls are only read/run during the execution phase. Due to this code processing order, we can do things like this: 
```js
    belowCall()
    
    function belowCall(){
        console.log("I was called before I was declared!")
    }
    
    //OUTPUT:
    > undefined
    > I was called before I was declared!
   ``` 
    

Why does this work? Because during compiling phase, `function` calls are essentially invisible. The compiling phase skips over all `function` calls, reads for what code to execute *when* they are called, and then the calls are read and run during execution phase. 

However, if you were to try this with a variable pointing to your function (a function expression), you'll run into trouble: 
```js
    varFunction();
    
    var varFunction = function(){
    	console.log("I was called before I was assigned!")
    }
    
    //OUTPUT:
    > TypeError: varFunction is not a function
  ```  

What the heck!? Here's what the heck: 
```js
    // How the compiler reads the code above: 
    
    var varFunction; 
    
    varFunction(); 
    
    varFunction = function(){
    	console.log("I was called before I was assigned!")
    }
   ``` 

Remember! Variable assignment is read *during execution phase,* but *after function calls*. 

What's happening above is that we are telling our code that we have a `var` declaration called `varFunction`, we attempt to call `varFunction()`, and *then* we tell `varFunction` what it points to (a function). 

At the time the code is run, our JavaScript program doesn't yet know that `varFunction` *is a function expression*, only that it's a variable that exists. So rather than coming back as `undefined` like our previous `var` declarations, JavaScript goes "Hey, you told me to call this function but you haven't told me about it yet, so I'm mad at you!" 

OK. So, maybe it's `var`'s fault? Let's try using `let` instead... 
```js
    thisFunction();
    
    let thisFunction = function(){
        console.log("I was also called before I was assigned!")
    }
    
    //OUTPUT: 
    > ReferenceError: can't access lexical declaration `thisFunction' before initialization
```
That doesn't work either! 

This is at least a little more helpful, though, since the error Javascript is giving us is pretty much saying "Hey, it looks like you put some stuff in the wrong order". You still cannot assign a function expression after calling it, but when using `let` your error message at least provides a little bit more insight. 

( As a side note, this is the same error you would get when attempting to utilize any other `let` variable in your code before it has been assigned **AND** is the same error you'll receive if you try to do something similar with a `const` declaration/assignment ) 

**Important Takeaways:** 

*NEVER* use `var` declarations. Just...don't do it. They will wreak havoc, and have been replaced and outmoded by the much improved `let` and `const` declarations. 

*Remember the compilation/execution order*: Function declarations > variable declarations > function calls > variable assignment. This order helps give you a sense of what will be hoisted where in your code during the compiling and execution phases.