var, let and const : ✨demystified✨
Tags: javascript, beginners, tutorial, programming
Published: 2022-03-25 03:48:06

Hello folks. Welcome to my another post.

In this post, we will learn about `var`, `let` and `const` from the basics to the magical parts of it. So please bear with me till the end.

### Variables
- Variables in programming languages are means to store information in memory and assign a human-readable label to it for future references.
- There are a few rules for variable naming:
  - The name must contain only letters, digits, or the symbols $ and _
  - The first character must not be a digit
  - It can't contain any whitespace characters
  - There are some [reserved](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords) words that can't be used as variable names
- In Javascript, there are two types of variables: Primitives and Reference types. Boolean, string and number are examples of primitive types while objects and arrays are examples of reference type. 
- Javascript is a dynamically typed language. That means we can assign different types to different variables without having an error(for `var` and `let` keywords and not `const`)
- In Javascript, we can declare a variable using `var`, `let` and `const`.

---

### Some of the terms that I will be using in this post
#### Scope
- Scope in Javascript refers to the variable's accessibility in the code. Based on the scope of a variable, some variables can be accessed in some part of the code while some can't be accessed in that part of the code.
- There are three types of scopes: Global, Function and Block.
  - Variables declared at the top level(outside any function) are global scoped. They can be accessed throughout the program.
  - Variables declared inside a function are function scoped and can only be accessed inside that function. It will throw a reference error if tried to access outside the function.
  - Variables declared inside `{}` are called block scoped and their accessibility depends on the keyword that was used to declare them(from `var`, `let` and `const`).

#### Scope chain 
- Javascript creates scopes for every executing function and `{}` block. There is also a global scope that holds some special values and variables that are in the global scope. 
- Each scope has access to the parent scope in which it is defined. By using it, the current scope can access the variables from the parent scope. This creates a chain of scope which is called a scope chain.

#### Hoisting
- JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, before execution of the code.

I won't be going deep into any of these topics right now. (Maybe in the future posts 😉)

---

Now let's learn about `var`, `let` and `const`.

### var
- The `var` keyword is an old way of creating variables in Javascript.
- Javascript engine doesn't throw an error if we try to create two variables of the same name in the same scope using `var`. If the second statement is an assignment then it will replace the value within the variable. If the second statement is just a declaration then it will be ignored. Javascript engine won't throw an error here.
```javascript
var test = "Hello";
var test;
console.log(test); // Hello
var test = "Nice";
console.log(test); // Nice
```
- The scope of a variable declared with `var` is its current execution context and closures(Maybe in the future post). In simpler words, `var` declarations are function scoped and accessible inside that function and variables that are declared in the global scope are accessible anywhere.
```javascript
function testFn() {
  var test1 = "Hello";
  if (true) {
    var test2 = "Nice";
  }
  console.log(test1, test2); // Hello Nice
  function innerFn() {
    var test3 = "Wow";
    console.log(test1, test2); // Hello Nice
  }
  // test3 is not accessible here.
  // It will throw a ReferenceError.
}
testFn();

// test1, test2 and test3 are not accessible here. 
// They will throw a ReferenceError.
```
- When using `var`, we can modify or re-assign any type of primitive values or reference values.
```javascript
var test = "Hello";
test = "Nice";
console.log(test); // Nice
test = 5;
console.log(test); // 5
test = ["wow"];
console.log(test); // ["wow"]
```
- `var` declarations are hoisted and initialized with the value `undefined`. What this means is that we can use a variable before it is declared but it won't have any value till any one of the assignment statement gets executed.
```javascript
console.log(test); // undefined
// It didn't throw an error 🙂
var test = "Hello";
```
---

### let
- `let` keyword is used for creating block scoped variables.
- Unlike `var`, we can't have two variable declarations using `let` with the same name inside the same scope. It will throw an error.
```javascript
let test = "Hello";
let test = "Bad"; 
// SyntaxError: Identifier 'test' has already been declared
```
- The scope of a variable declared with `let` is the curly brackets containing the variable and for the global scope, it is accessible after the declaration throughout the program.
```javascript
if (true) {
  let test = "Hello";
  console.log(test); // Hello
}
console.log(test); // ReferenceError: test is not defined
```
- `let` declarations are also hoisted but not initialized. That means accessing a variable before its declaration will throw an error.
```javascript
console.log(test); // ReferenceError: test is not defined
let test = "Hello";
```
- Same as `var`, when using `let`, we can modify or re-assign any type of primitive values or reference values.

---

### const

- `let` and `const` are the same. The only difference is in the modification and re-assignment of the variable.
- All the variables declared using `const` and having a primitive value can't be modified or re-assigned. It will throw an error if tried to do so.
```javascript
const test = "Hello";
test = "Wow"; // TypeError: Assignment to constant variable.
```
- All the variables declared using `const` and having a reference type value, can be modified but can't be re-assigned.
```javascript
const test = ["Hello"];
test.push("World");
console.log(test); // ["Hello", "World"]
test = ["Wow"]; // TypeError: Assignment to constant variable.
```
---

Now let's demystify some of the magical cases.

### Case 1
- Let's try to assign a value to a variable before its declaration with `let`(or`const`) and see what happens.
```javascript
test = "Bad"; 
// ReferenceError: Cannot access 'test' before initialization
let test = "Hello"; 
```
- As expected, this gives an error. But a lot is going on here and let's try to understand it.
- Here `test` is declared using `let`, so it will get hoisted, but it won't get initialized. Since it doesn't get initialized, trying to assign it a value will give an error "Cannot access 'test' before initialization".
- Now let's try to do the same thing with `var` and see what happens.
```javascript
console.log(test); // undefined
test = "Wow";
console.log(test); // Wow
let test = "Hello";
console.log(test); // Hello 
```
- Here `var` declaration is first hoisted and then initialized with the `undefined` value which is why the first console will print `undefined`.
- Then as the variable is initialized assigning a value `Wow` to it works fine and the second console prints `Wow`.
- When the Javascript engine comes to the `let` declaration it simply assigns the value `Hello` to it and that is why the third console prints `Hello`.

---

### Case 2
- Let's see an interesting case with hoisting and variable shadowing. 
```javascript
let test = "Hello";
if (true) {
  let test = "Wow"; // Will this throw an error???
  console.log(test); // Will this execute???
}
console.log(test);
```
- Let's try to dissect it.
- Here we have declared a variable named `test` and initialized it with the value `Hello`.
- Then when it enters the `if` block, it will create a new scope. As always, Javascript will hoist the declaration of the `test` variable and it won't get initialized as it is declared using `let`.
- Then the Javascript engine will assign it the value `Wow`. It will work as the `let` is block scoped and Javascript can have the same named variables in different scopes.
- Now when we reach the console Javascript engine will try to find the variable in the current scope and as the current scope has the variable with the name `test` it will use it and it won't use the variable from the parent scope. This is called variable shadowing.
- As the inner variable's scope is over with `if`'s curly brackets, the last console will print `Hello`.
- Let's look at an example with a small variation.
```javascript
let test = "Hello";
if (true) {
  console.log(test); // 🤔
  let test = "Wow";
  console.log(test);
}
console.log(test);
```
- Here when the Javascript engine enters the `if` block, it will create a new scope. As always Javascript engine will hoist the declaration of the `test` variable and it won't be initialized as it is declared using `let`.
- So as we can guess now there is a variable with an uninitialized state in the current scope so Javascript won't use the parent value and throw `ReferenceError: Cannot access 'test' before initialization`.
- Now let's look at the same example using `var`
```javascript
var test = "Hello";
if (true) {
  console.log(test); // 🤔
  var test = "Wow";
  console.log(test);
}
console.log(test);
```
- Here when the Javascript engine enters the `if` block, it will create a new scope. As always Javascript will try to hoist the declaration of the `test` variable but the variables declared using `var` are not block scoped, they are function scoped.
- Javascript engine will not hoist it as a same named variable is already there in the current scope. So the first console will use the value from the parent which is `Hello`.
- When the engine reaches the declaration of the `test` variable inside the `if` block it is treated as the declaration of the same named variable as the `var` is function scoped and the engine will simply assign the value `Wow` to the `test` variable and the second console will print `Wow`.
- As the parent variable is re-assigned with the new value, the third console will also print `Wow`.

---

Bear with me there is more 😁

### Case 3
- Let's look at an interesting case of `var` inside the `if` block.
```javascript
if (false) {
  var test = "Hello";
}
console.log(test); // Reference error??? 🤔
```
- Here as we can see that the if block doesn't get executed as the condition, is false, so it should throw a Reference error. Right? Right???
- Well here it won't throw a Reference error and instead, it prints `undefined` 🙂.
- The reason for this is that the Javascript engine still hoists the `test` variable even if this code doesn't get executed and our global scope is now polluted with an extra unnecessary variable. One of the reasons why you should avoid using `var` 😅.
- In the older code you may see an interesting pattern called IIFE - Immediately Invoked Function Expression through which people avoided the scope pollution.
```javascript
if (false) { // or true
  (function () {
    var test = "Hello";
    // Some code that uses test
  })(); // Note the invocation here
}
console.log(test); // ReferenceError: test is not defined
```
- Here we have created an anonymous function and immediately called it. Javascript treats it as an expression(thus IIFE). 
- As we know that the `var` is function scoped and thus it can't be accessed outside the anonymous function.

---

### Case 4
- Let's look at some of the weird cases of the variables declared using `var` in the case of `for` loops. Let's start with a simple example.
```javascript
for (var i = 0; i < 3; i++) {
  // Do something
}
console.log(i); // 3
```
- As we can see here that the console prints the value `3` and that is because the variables declared using `var` are function or global scoped and not block scoped. So here `i` is accessible even after the `for` loop. Again scope pollution 🙂.
- Let's look at another famous `for` loop problem with `var`
```javascript
var fnArray = [];
for (var i = 0; i < 3; i++) {
  fnArray[i] = function () {
    console.log(i);
  };
}
for (var j = 0; j < 3; j++) {
  fnArray[j]();
} // 0, 1 and 2 ??? 🙂
```
- Here we may think that it should print `0`, `1` and `2` but it won't and let me tell you why.
- Here we have created an array named fnArray and we have pushed some functions in it which is using the variable `i` from the `for` loop.
- We know that `var` is function scoped so its accessibility doesn't have to do anything with the `for` loop. The function is using the variable `i` but it will only access its value when it is being executed.
- In the last iteration of the first `for` loop, `i++` will be executed with the value `2` and it will become `3` which will stop the loop. Now variable `i` will be accessible outside the `for` loop with value `3`.
- Now when the second `for` loop gets executed, it will call the anonymous function which will try to console the value of the variable `i` and as the value of `i` is now `3` it will print `3` three times.
- This problem can be solved easily by using `let` in the first `for` loop.
```javascript
var fnArray = [];
for (let i = 0; i < 3; i++) {
  fnArray[i] = function () {
    console.log(i);
  };
}
for (var j = 0; j < 3; j++) {
  fnArray[j]();
} // 0, 1 and 2 as expected
```
- This will work because the `let` variables are block scoped. So each iteration of the `for` loop will create a scope and it will hold the value of `i` for that iteration.
- So when the function will try to access the value of `i`, it will see the correct value in the scope created by the `for` loop and print `0`, `1` and `2` as expected.

---
### Summary
![Summary of var, let and const](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rukmywpfanoqy4iy4g0a.png)

---

So that's it for today folks 😅. 
Thanks for bearing with me till the end. Give the post a Heart if you liked the post and give a comment or ping me in case I have missed anything.

---
You can reach me on:
- [LinkedIn](https://www.linkedin.com/in/yash-kalaria-50388b131)
- [Github](https://github.com/PRO-KlauS)
- Email : yash.kalaria93@gmail.com