ES6 - A beginners guide - New Variable scoping (Let and Const)
Tags: javascript, beginners, codenewbie, webdev
Published: 2021-09-02 11:03:52

Welcome back! It's great to see you on this entry in the series. This time we are going to discuss the new variable scopes called `let` and `const`. In ES6 we can essentially forget about `var` and just use our new friends `let` and `const` to have much more control over our variables, ensure that they contain the exact data that we expect at the very point in our code execution.

## So what are they?

Although less commonly used now with the introduction of ES6 we'll include `var` in the explanations below.

### var

`var` can be seen as the most relaxed kind of declaration, it is left open for redeclaration, and also redefinition. For example:

```javascript
var a = 1; // window.a = 1
var a = 2; // window.a = 2

var b = 1; // window.b = 1
b = 2; // window.b = 2
```

Neither of these will cause the browser to throw an error or exception. Let's have a look at another `var` example which you may come across:

```javascript
var a = 1;
function logVariable() {
  console.log(a);
  var a = 2;
}
logVariable(); // Returns undefined in a console log
console.log(a); // Returns 1 in a console.log
```

Wait...what? How can we reference a before declaring it? This is what is known as Hoisting. Hoisting is where the JavaScript engine processes the var delcarations during compilation time but it does not assign it a value until the expression is executed so until that time you receive undefined as the return value.
Also, notice how the final console.log returns 1? This is because the first var is globally scoped, and the second is function scoped. So even though in the function we set a = 2, that was in the function scope and would only output 2 if we set a console.log after the assignment in the functions. As we had already set a = 1 at the global scope level when we console.log that outside the function it will use the global `var`.

### So, what about let?

`let` is block scoped and so applies to everything inside

```javascript
let a = 1;
console.log(a); // Returns 1 in a console.log
const logVariable = () => {
  console.log(a); // Uncaught ReferenceError
  let a = 2;
};
logVariable(); // Throws an exception
console.log(a); // Doesn't run because of the exception
```

If you try to run the JavaScript above you will find that it throws an Uncaught ReferenceError, this is because whilst `let`'s are hoisted but not initialized, they live in a "Temporal Dead Zone" (TDZ) meaning we cannot actually access it, thus throwing the ReferenceError. 

Patricia has some great descriptions of Hoisting and the TDZ in her article here:
{% link pat_the99/javascript-hoisting-450l#tdz %}
When we hit an exception like we see in the above example it stops the rest of the JavaScript in that block from functioning and thus we do not see the final console.log().

It is worth noting that `let`'s can be re-assigned but not re-declared, for example if we re-assign the variable:
```javascript
let a = 1;
a = 2;
console.log(a); // Returns 2 in a console.log
```
but if we try to redeclare the variable as we see below, it will throw an exception:
```javascript
let b = 1;
let b = 2;
console.log(b); // Throws a SyntaxError because b has already been declared
```

Interestingly, if you run both of the above snippets at the same time, neither console.log's will output anything despite one of them referencing a variable already defined and assigned, this again is because of hoisting. The declarations are hoisted to the top, and the JS engine detects that there are two declarations for `b` throws the exception before attempting to execute anything in that block of code.

### How about const?
The introduction of `const` is a nice one. It allow's us to add a level of security to our variables knowing they cannot be changed, well the changes are restricted...i'll go into that in a bit though. As with `let`, `const`'s are hoisted and will also land in the TDZ during compilation, they also cannot be redeclared and are not available in the global scope.
A key difference between `let` and `const` is that `const` requires assignment at the point of declaration, you cannot create a `const` and then give it a value. Once given a value, that value is constant (almost).
```javascript
const a; // Uncaught SyntaxError: Missing initializer in const declaration
const b = 1
b = 2 // Uncaught TypeError: Assignment to constant variable.
```
I mentioned above that changes to a `const` are restricted as opposed to flat-out saying they cannot change. Look at the below example, you'll see that I create a `const` which is assigned an object (array's behave the same here), I can modify the contents of the object/array but I cannot completely change the assignment of the const itself. Let's get an example using an object:
```javascript
const obj = {name: "Stefan"};
obj = {}; // Uncaught TypeError: Assignment to constant variable.
obj.name = "Bob";
console.log(obj) // Returns Bob in a console.log
```
and an example using an array:
```javascript
const arr = [1, 2, 3];
arr = [] // Uncaught TypeError: Assignment to constant variable.
arr.push(4) // You can push into a const array
arr[0] = 11 // You can also modify at the point of an array
console.log(arr) // Returns [11, 2, 3, 4] in a console.log

```
#### Why??
Ultimately, `const` is a "Constant Reference" as opposed to a "Constant Value", this is because the declaration and assignment of a const is to a point in memory. Depending on the data type depends if the reference value is mutable or not. When you assign a string, boolen, number, or maybe even a function you are assigning a primitive value. When you assign an Object or an Array these are non-primitive. The assignment will be protected and y but the data inside it will not be protected. Primitive values are immutable whereas Objects and Arrays are mutable (can be changed). If you are using a const with an Object, and you want to lock those values in, you can use Object.freeze() to do this, see this example:
```javascript
const obj = Object.freeze({name: "Stefan"});
obj.name = "Bob"; 
// If you are setting 'use strict' in your code then you will see
// Uncaught TypeError: Cannot assign to read only property 'name' of object '#<Object>'
// Else it will silently fail
console.log(obj) // Returns Stefan in a console.log
```