var, let or const: what's the real difference?
Tags: javascript, webdev, 100daysofcode, beginners
Published: 2023-03-01 13:31:11

## The `var`, `let` and `const` keywords
At this point, the `let` and `const` keywords are pretty much ubiquitous. Before their existence, we could only use the `var` keyword to declare variables which store our data. Although you will still encounter `var` in JavaScript code today, `let` and `const` bring with them some features which help address certain issues you might encounter when using `var`.

### Scope
One of the differences between `let`/ `const` and `var` relates to how they handle **scope**.  

Scope refers to the accessibility of variables and functions in different parts of a program. Being a lexically scoped language, the accessibility of variables and functions in JavaScript depends on where they are **declared** in the program. Both `let` and `const` are **block scoped** whereas `var` is not. This means that variables declared with `let` and `const` within if/switch statements, for/while loops and curly braces `{ }` **are not accessible outside of this block**.

Let’s see how this block-scoped behaviour affects our code:

```javascript

for (var i = 0; i < 3; i++) {
  console.log(i)
}

console.log(`var i = ${i}`)


for (let j = 0; j < 3 ; j++) {
  console.log(j)
}

console.log(`var j = ${j}`)

// OUTPUT -->
// (first for loop - using var)
// 0
// 1
// 2
// "var i = 3"
// (second for loop - using let)
// 0
// 1
// 2
// "error"
// "ReferenceError: j is not defined"

```

In the above example we have two for loops. In the first loop, we use the `var` keyword to declare variable `i`. Despite the fact that `i` is within a for loop, we are still able to access it outside the for loop. 
In the second loop we use the `let` keyword to declare variable `j` . We try to access `j` outside of the for loop but we receive the `ReferenceError: j is not defined`. As it was declared using let, it is block scoped and therefore only accessible within the loop. 

This same behaviour occurs when using the `const` keyword to declare variables within block scopes:

```javascript

for (let i = 0; i < 3; i++) {
  const myName = 'Pippa';
  console.log(`${myName} -> ${i}`)
}

console.log(myName);

// OUTPUT -->
// (console.logging within the for loop)
// "Pippa -> 0"
// "Pippa -> 1"
// "Pippa -> 2"
// (console.logging outside the for loop)
// "error"
// "ReferenceError: myName is not defined"

```
Here we have declared the variable `myName` using `const` inside a for loop. Although we can access `myName` inside the for loop as demonstrated by the `console.log(${myName} -> ${i})`, we cannot access it from outside the loop and we receive a `ReferenceError` when we try. 

### Redeclaration and Reassignment
Another way in which `let` / `const` differ from `var` relates to whether or not they can be redeclared or their values reassigned. Variables declared with `var` can be both redeclared and reassigned. 

```javascript

var myName = 'Pippa';

console.log(myName);

var myName = 'Philippa';

console.log(myName);

myName = 'Pippa Again'

console.log(myName)

// OUTPUT --> 
// "Pippa"
// "Philippa"
// "Pippa Again"

// No errors, we can declare myName, redeclare it and finally reassign the value.

```

This can sometimes cause issues, especially when working with global variables. It’s one of the reasons that `let` and `const` were introduced because when using `var` you could end up accidentally redeclaring variables. 

Let’s try the same using the `let` keyword:

```javascript

let myName = 'Pippa';

console.log(myName);

let myName = 'Philippa';

console.log(myName);

// OUTPUT -->
/// "SyntaxError: Identifier 'myName' has already been declared"

```

Here, we receive a syntax error informing us that the `myName` variable has already been declared. So with `let`, **we cannot redeclare a variable**. We can, however, reassign values:

```javascript

let myName = 'Pippa';

console.log(myName);

myName = 'Philippa';

console.log(myName);

// OUTPUT -->
// "Pippa"
// "Philippa"

```
No errors! Rather than redeclaring `myName` we just reassign its value, which is fine by `let`. 

So how does `const` deal with redeclaration of variables? `const`, short for constant, displays the same behaviour as `let` when it comes to redeclaring variable, i.e. **it does not allow variable redeclaration**:

```javascript

const myName = 'Pippa';

console.log(myName);

const myName = 'Philippa';

console.log(myName)

// OUTPUT -->
// "Pippa"
// "SyntaxError: Identifier 'myName' has already been declared

```
The same as with `let`, we receive a `SyntaxError` when trying to redeclare a `const` variable. But what about reassigning the value of a `const`? 

```javascript

const myName = 'Pippa';

console.log(myName);

myName = 'Philippa';

console.log(myName)

// OUTPUT -->
// "Pippa"
// "error"
// "TypeError: Assignment to constant variable.

```

That’s also a big no-no. This time we receive a `TypeError` telling us we have assigned a constant variable, which cannot be changed. As such, `const` is a good option to use for variables which you know won’t change. 

While useful in some cases, doesn’t this behaviour seem a bit inflexible? Let me expand a bit more on the details of this. As we just saw,  when using `const` to store primitive data types (`Number`, `String`, `Boolean`, `null`, `undefined`, `BigInt` and `Symbol`) we cannot redeclare or reassign the value. However, this is not the case when using `const` to store non-primitive data types (objects, which include arrays and functions). 

When storing non-primitive data types, although we cannot redeclare them, we can mutate them. An example of this would be using `const` to store an object and then later adding on a new property or method. 

The reason for this relates to how JavaScript stores different types of data. When we store an object (or any non-primitive data type) in a variable, for example `const myObject = {...}`, we are saving a reference to the object (essentially an “address” to a point in memory in the JavaScript memory heap) rather than storing the object itself. Primitive types in JavaScript are stored in a different way; the actual value is stored as opposed to a reference to a point in memory.

This means that we can use `const` to store an object, and later mutate this object as it is a non-primitive data type:

```javascript

const carObject = {
  'make': 'Ford',
  'wheels': 4
}

console.log(carObject);

carObject.colour = 'red';

console.log(carObject);

// OUTPUT -->
/*
FIRST CONSOLE.LOG()
[object Object] {
  make: "Ford",
  wheels: 4
}
SECOND CONSOLE.LOG()
[object Object] {
  colour: "red",
  make: "Ford",
  wheels: 4
}
*/

```

As you can see, we declared `carObject` using `const` and were able to add the colour property on later as the **reference to the object** which was saved in `carObject` **has not been changed**.

### Hoisting
Another major difference between `let`/ `const` and `var` is their behaviour relating to hoisting.

Before we dive into these differences, let’s first discuss what hoisting actually is. To do so we must take a look at the 2 phases of the JavaScript execution context.

When a program is passed to a JavaScript engine there are 2 phases which will occur. JavaScript will first parse the file in what it known as the **creation phase**. During this phase, JavaScript will allocate memory for all variables and functions, amongst other processes such as creating the execution context and the scope chain.  

Next comes the **execution phase** during which, as the name suggests, the JavaScript engine actually executes the code in the file. 

Hoisting happens during the **creation phase**, and it is the process of JavaScript identifying any variables and functions in a file and “hoisting” them to the top of their respective scope so that they are available for use during the execution phase. However, as we have already discovered, not all variables are created equal, so how does the behaviour of `let`/`const` differ from `var`? Let’s take a look.

#### Var
Any variable that has been declared with `var` will be partially hoisted. The identifier name will be stored in memory but the value will be set to `undefined` until JavaScript reaches the assignment of the value during the execution phase. 

```javascript

console.log(myVar)

var myVar = 'I used to be undefined';

console.log(myVar)

// OUTPUT -->
// undefined | first console.log()
// "I used to be undefined" | second console.log()

```

No errors were thrown, but the value of `myVar` was set to `undefined` until the execution phase during which JavaScript assigns the value as the string ‘I used to be undefined’. 

#### Let and Const
Variables declared with `let` and `const` will be hoisted, but their values will be set to `uninitialised`. This is different from `var` as any values declared by var will initially be stored as `undefined` until the execution phase. 

Let’s take a closer look at using let:

```javascript

console.log(myLet)

let myLet = 'I used to be uninitialised';

console.log(myLet);

// OUTPUT --> 
// "error" 
// "ReferenceError: Cannot access 'myLet' before initialization

```

The reference error message we received confirms this; `Cannot access 'myLet' before intialization`. So, JavaScript is aware of its existence, but it has not yet been initialised with any value.  Practically speaking, what this means is that you should **declare and assign** the value when using `let` **before** you try and access it in your code - unless you want to receive an error that is!

That being said, some potentially unexpected behaviour occurs when we: 

+ declare a variable with `let` but don’t assign it a value
+ access that variable
+ then assign it a value

That sounds a bit confusing, so let me show you what I mean with an example:

```javascript

let partiallyHoisted;
// declare a variable using let but don't manually assign it a value

console.log(partiallyHoisted);
// access the variable 

partiallyHoisted = 'I used to be undefined';
// assign a value to the variable

console.log(partiallyHoisted)

// OUTPUT -->
// undefined | first console.log()
// "I used to be undefined" | second console.log() after assigning the variable a value

```

No errors! In this case, although we tried to access `partiallyHoisted` before we assigned it a value, as we had declared it (and therefore JavaScript “knew” it existed) it is set to `undefined` by default, until we manually assign it a value.

This behaviour differs between `let` and `const`. When using `const` we cannot declare a variable without assigning it a value, and if you try you will receive a syntax error: 

```javascript

const myConst;

console.log(myConst);

myConst = 'This will cause an error!';

console.log(myConst)

// OUTPUT --> 
// "error"
// "SyntaxError: Missing initializer in const declaration"

```

Aside from the example we just looked at, `const` behaves the same way as `let` when it comes to hoisting in JavaScript. The identifier name will be hoisted to the top of its scope, but its value will be uninitialised and therefore inaccessible until JavaScript reaches the line of code which assigns it a value during the execution phase. 

```javascript 

console.log(myConst);

const myConst = 'I will be uninitialised ';

console.log(myConst);

// OUTPUT -->
// "error"
// "ReferenceError: Cannot access 'myConst' before initialization"

```

If we take a look at the error message we receive, we see that it’s the same error we got when trying to access a variable declared with `let` before assigning it a value. It cannot be accessed as it’s not yet been initialised. There’s a term for this specific period during which `let` and `const` variables have been hoisted (so JavaScript “knows” they exist) but not initialised so are therefore inaccessible; the **Temporal Dead Zone**.

As a final note, although you will continue to see the use of the `var` keyword in JavaScript code, `let` and `const` are nowadays considered standard, with `const` usually being used to store objects and arrays even if you plan on mutating them later on in the program. 

If you made it this far, thanks for reading!