Tricky bits of JavaScript
Tags: codenewbie
Published: 2020-06-26 15:59:21

######Photo by <a href="/photographer/artM-56112">artM</a> from <a href="https://freeimages.com/">FreeImages</a>

When learning a new language, it makes sense that some parts will seem kinda familiar while others are quite mysterious. I'm studying JavaScript now and there are a few tricky bits I'd like to review in more detail. 

          var, let, const  ◆  hoisting  ◆  scope  ◆  closure

Before we go into the topics above, let's take a look into how JavaScript is working 'behind the scenes'. The JavaScript Engine actually makes two different passes over your code and in the first pass, called the Creation phase, the code is not executed line by line. Instead, a global object and an associated `this` are created, and variables get set up in memory. During the second pass is where the work begins, called the Execution phase because this is when your code is actually run line-by-line and values are set to the variables previously defined and stored in memory during the first pass.  

Having a good understanding of how the JavaScript engine actually evaluates your code is a valuable tool. This will be more evident as we explore the topics mentioned inside the box at the top of this post. 
***
Prior to the release of ES6, `var` was the only choice for variable declaration. As it turns out, `var` can cause some trouble in your code due to its flexible nature, and that is where `let` and `const` come to the rescue. Let's take a look at each variable declaration type a bit more closely.

Before we go over `var`, `let`, and `const`, it is important to review the concept of _scope_. Scope is defined as "the current context of execution" and is basically where a variable is available to use. In the case of `var`, it is function scoped when declared within a function and global scoped when declared outside a function. Function scoped means that the variable is accessible and available within that function only. This is quite different from global scoping where the variable becomes available to the entire window. 
Consider this example:

```javascript
   var mood = 'happy';

   function changeMood() {
     var newMood = 'curious';
     console.log(newMood);
   }

   console.log(mood);
   changeMood();
```
Here, the first variable declaration is within the global scope, so it is available for use anywhere within the window. When you `console.log(mood)`, the output is 'happy', accessed from the global scope. 

The second variable declaration, inside the `changeMood()` function, exhibits functional scope behavior. When `changeMood()` is called, the output is 'curious' since the variable `newMood` was declared within the function. It is available to the function itself, but if you tried to `console.log(newMood)` from outside of the function, you get: `ReferenceError: newMood is not defined`. The scope of `newMood` is only within the `changeMood()` function. 

In the case of `let` and `const`, variable declarations using these keywords are block scoped only. This means that variable declarations can only be accessed within the block (inside the curly brackets only) from which they were declared. Keeping variable declarations out of the global scope helps to prevent bugs and issues with overwriting variables previously declared. 

Now that we've briefly covered _scope_, let's take a look at some of the behaviors of `var` that led to the need for `let` and `const`. When you use `var` to declare a variable, JavaScript won't prevent you from redeclaring that variable, in fact it won't even let you know that you have overwritten a previously declared variable. Oh no!

```javascript
  var cat = 'Coco';
  var cat = 'Beebee';

  console.log(cat);  //=> Beebee
``` 
The output for `cat` is 'Beebee' since `var` allows for redeclaration of variables. This is an easy way to put bugs into your program and should be avoided. Take a look at how `let` and `const` prevent this down below: 

```javascript
  let cat = 'Coco';                  const cat = 'Coco';
  let cat = 'Beebee';                const cat = 'Beebee';

  console.log(cat);                  console.log(cat);

  `SyntaxError: Identifier 'cat' has already been declared`
``` 
Also note that `var` and `let` variables can be updated, whereas `const` can only be defined once and may not be updated. 
***
Remember how the JavaScript engine processes your code in two passes? Good, let's put that knowledge to use. _Hoisting_ is defined as when variables and function declarations are 'moved' to the top of scope before execution. Well, they aren't really physically moved, but during the Creation phase they are set into memory and variables declared with `var` are initialized with a value of `undefined`. Variables declared with `let` and `const` are not initialized with a value, but are still set into memory. So if you try to use a variable with `let` or `const` before it is declared, a `Reference Error` occurs. Let's take a look at some examples:

```javascript
    console.log (greeting);
    var greeting = "hola"
```
Will be interpreted like this:
```javascript
    var greeting;
    console.log (greeting); //=> greeting is undefined
    greeting = "hola"
```
And when using `let` or `const`:

```javascript
  console.log (greeting);         console.log (greeting);
  let greeting = "hola";          const greeting = "hola";

ReferenceError: Cannot access 'greeting' before initialization
```
An interesting note about `const`:
Variables declared with `const` must be initialized at the time of declaration.

```javascript
   const greeting;
   console.log (greeting);
   greeting = "hola";

   SyntaxError: Missing initializer in const declaration
```
> So in summary:

|      |Scope     |Update?|Re-declare?|Hoisted?|Initialized|
| -----|:--------:| -----:|:---------:|:------:|:---------:|
| var  | global or functional|  yes  |   yes    |  yes   | undefined|
| let  | block    |  yes  |   no      |  yes   | no      |
| const| block    |  no   |   no      |  yes   | required|


The last topic I would like to cover here is _closure_. Closure is when an inner function is able to access the outer function's variables, creating a scope chain. This should make sense now that we understand _scope_ and _hoisting_. The following example should be helpful:

```javascript
let learn = 'learning experience! ';
let result;
function phrase(a){
  return function(b) {
    return (a + b + learn)
  }
}

result = phrase('Enjoy ')('your ');
console.log(result); //=> Enjoy your learning experience! 
```
The innermost return of this function uses variables that have been declared outside of the inner function. This is possible because the scope chain is maintained by the inner function. When the function is executed, this scope chain allows access to the variables that live inside the outer function. This example would also work with `var` (but don't!) and will not work with `const` due to the need to initialize variables using `const` at the time of declaration.

Hopefully this helps to clarify some of the more mysterious parts of JavaScript functionality. Understanding these tricky parts will help you write better code!