[freeCodeCamp] ES6 - var, let and const
Tags: javascript, 100daysofcode, react, webdev
Published: 2020-08-08 09:08:31

Hi there folks! Continuing the JavaScript learning from [freeCodeCamp](https://www.freecodecamp.org/), this time we move ahead from the Basic JavaScript and would start with the ES6. ECMAScript 6 or ES6 is the major JS version which introduced a variety of helpful features, released in 2015. Going with this, we will explore those features and how to use them in upcoming posts as well.

This post mainly covers the caveats regarding the variable declaration and how ES6 changes its behaviour. So, let's start without delaying anything.

# <u>Difference between `var` and `let` keywords</u>

The usage of `var` had some issues, which we'll come across little by little. However, one potential issue is that variables declared with `var` can be overridden without any errors. For e.g.

```
var myName = Prashant;
var myName = Akash; 
console.log(myName);  // Akash
```

As you may see, we've overridden the variable `myName`, but JavaScript didn't raise any error. With application with larger source code, we may accidentally overwrite a variable, which we don't intend do. This could lead to unexpected behaviours and difficulties in debugging.

To resolve this, ES6 provides the keyword `let` for variable declaration. If we were to use `let` instead of `var` in the above example, the second initialization would lead to an error.

> We can also use the `"use_strict"` to enable the strict mode. This would catch common mistakes and unsafe actions.
For e.g.
> 
> `"use strict";`
> `x = 3.14; // throws an error because x is not declared`

## <u>Scope differences between `var` and `let` keywords</u>

If we go back and try to remember the behaviour of the `var` keyword w.r.t scopes, we know that any variable declared with it is global and if declared within a function, it's scope is limited to that function only.

`let` behaves in a similar way, but with some extra features. For e.g. when we declare a loop variable using the `var` keyword, that becomes global. However, declaring the same, using `let` would result in its scope limited to the block of the loop.

Let's see an example

```
arr = [1, 2, 3, 4];
for (var i = 0; i < arr.length; i++) {
   arr[i]++;
}
console.log(i);  // returns 4
```

You may notice that the variable `i` is accessible outside the loop. If you were to use it again elsewhere, it'd be using the updated value, which might result in unexpected behaviour. However, with `let`, this doesn't happen i.e. the loop variable `i` will only be accessible within the loop block and not outside of it.

Let's see another example

```
function checkScope() {
  'use strict';
  let i = 'function scope';
  if (true) {
    let i = 'block scope';
    console.log('Block scope i is: ', i);
  }
  console.log('Function scope i is: ', i);
  return i;
}
```

Before we discuss the output of the above code, a doubt may arise to you. You may ask, that `let` should not allow the re-declaration of the variable `i` at all. The catch here is that it restricts the same declaration within the same scope. In the above piece of code, the second initialization is within a local scope. Hence `let` does not raise any error. 

Yes, if you would've tried to initialize it outside of the `if` block, it should have a complaint about that. Now, let's move to the output of the above code.

The output of this function would be

```
Block scope i is:  block scope
Function scope i is:  function scope
```

And can you guess which value of `i` would be returned? Think!

The answer is `function scope` would be returned, as the variable `i` within the local scope of `if` condition is not visible outside of its block. Hence the global `i` is returned.

What if I change the code slightly to this

```
function checkScope() {
  'use strict';
  let i = 'function scope';
  if (true) {
    i = 'block scope';
    console.log('Block scope i is: ', i);
  }
  console.log('Function scope i is: ', i);
  return i;
}
```

What do you think, would be returned now? Before that, did you notice the change? I removed the `let` keyword from inside of `if` block. That's it. Now, think!

This time, `block scope` would be returned. I see you ask why? Well, it's because this time instead of declaring a new variable with the same name in scope, we are instead, overriding the global variable `i`. Hence, the changes to it are reflected outside of the function.

# <u>Variable declaration using the `const` keyword</u>

ES6 not only provides the `let` keyword to declare a variable but also a `const` keyword to do so. The difference is that the variables declared with `const` are **read-only**. It means that once the variable is declared, it cannot be reassigned. These variable act as a constant value.

> It is a common practice to declare constants using the uppercase letters with words separated with underscores.

The use of `const` helps us to avoid accidentally changing the constants within a program. An example of its use can be seen as

```
const PI = 3.14;
PI = 2.17; // throws an error
```

## <u>Mutating arrays declared with const</u>

We should know that objects, arrays or functions would be mutable when assigned to a variable using `const`. It is actually the variable identifier, which cannot be reassigned.

Let's see an example for an array

```
const s = [4, 5, 6, 7];
s = [1, 2, 3]; // throws an error, as assignment to a const variable
s[4] = 8; // this would work fine
console.log(s);  // [4, 5, 6, 8]
```

The thing which is to be noticed here is that the values within the array are mutable. Even though we change the array elements, the variable `s` would still point to the same memory location. Only the value to that memory location has been changed.

Yes, if we would try to point `s` to some other memory location i.e. some other array or value, it would throw an error.

## <u>Preventing object mutation</u>

From the above, we found that `const` declaration doesn't actually protect the data from mutation. It only prevents the variable identifier to point to some other location instead.

However, if we want to prevent data mutation within an object, JavaScript provides the function `Object.freeze`.

After the object is frozen, any attempt to add, update or delete the properties would be rejected without any error.

```
function freezeObj() {
  let MATH_CONSTANTS = {
    PI: 3.14
  };

  Object.freeze(MATH_CONSTANTS);

  MATH_CONSTANTS.PI = 99;  // ignored and mutation won't be allowed
  return MATH_CONSTANTS.PI;
}
```

The above would still return 3.14. However, if you'd use the strict mode, it will throw an error.

# <u>Conclusion</u>

At the end of this post, we found the major differences between `var`, `let` and `const` keywords and where and why of using them. Further, we found that we can also prevent any data mutation using `Object.freeze()` method which comes in handy at times.

# <u>References</u>

- [Introduction to the ES6 Challenges](https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures/es6/)


We'll meet next time with other ES6 features so that we can improve our JS knowledge further. Till then be curious and keep learning. :)