The difference between var and let
Tags: javascript
Published: 2021-04-16 11:18:48

Var and let are two varible declaration types in javascript, that have lot of common behaviours. Though, there is some big differnces between this 2, and every javascript programmer needs to know them.

For understanding some of this differences, first we need to understand the difference between two javascript scopes : the **Function scope**, and the **Blocked scope**. **Function scope** is the area between the two curly braces that comes after the declaration:
`function func1(){
   //we are now inside the function scope...
}`

**Block scope** is the area between two curly braces **that not bind to a function**. For example:
`{let l1 = 'l1' //we just declared the varibale l1 inside a block scope}`
but also in:
```
if(2 > 1){
  let l2 = 'l2'
  console.log(l2)
  //we declared the variable l2 inside block scoped 
}
```

Both scope types have a lexical variables scope. it means that in both scopes, if we are declaring a variable inside of them, we will cannot be access this variables from an outer scope:
```
if(2 > 1){let l3 = 'l3'}
 console.log(l3) //Error, because we not recognize l3 outside of its scope
```
and also:
```
function func2(){let l4 = 'l4'}
func2()
console.log(l4) //Error, because we not recognize l4 outside of its scope
```

But, there is a little problem we didnt mention: **In a Block scope, a var declaration will leak out!**
Not like `const` or `let`, `var` that declares inside  a block scope will leak out to the outer scope. For example:
```
if(2 > 1){
  let l5 = 'l5'
  var v5 = 'v5'
}
console.log(l5) //Error, because we not recognize l5 outside of its scope
console.log(v5) //outputs 'v5'
```

So what happened in the code above?
We declared the variable l5 with the keyword `let`, and its remained inside is block scope.
We declared the variable v5 with the keyword `var`, and as we say before, it leaked out to the outer scope, (this case: the global scope).

Notice that this behaviour occurs only inside a **Block scope**.
in **Function scope**, the rules are still clear, and all the 3 keywords: `var`, `let`, and `const`, will remain in the **Function scope** and wont leak out:
```
function func3(){
  var v6 = 'v6'
  let l6 = 'l6' 
}
func3()
console.log(v6) //Error, because we not recognize v6 outside of its scope
console.log(l6) //Error, because we not recognize l6 outside of its scope
```

Another difference between `var` and `let` is the re-declaration.
in `var`, a declaration of variable with a name that is taken by another variable, will just apply the new value to the variable:
```
var v7 = 'v7'
var v7 = 'javascript'
console.log(v7) //outputs 'javascript'
```

in `let`, it wont happend, and this operation will cause an Error:
```
let l7 = 'l7'
let l7 = 'l8' //Error, the variable-name "l7" is already taken
``` 


Now when we know thes 2 diffrences we can discuss a famous Error that caused by the `var` keyword, and how we can fix it easily with the `let` keyword:
```
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 1000)
}
```

The output we except (after a second) is "0 1 2", but actually, the output we will get is "3 3 3" **Why is that?**

When we insert the first statement of the for loop (`var i = 0`),
the code that actually will be in the block of code inside will be something like this:
```
{
  var i = 0
  setTimeout(() => {
    console.log(i)
  }, 1000)
}
```
Moreover, if you noticed, this **code-block** is actually a **Block scope**, and its means that all the rules that we mentioned earlier are applies to here too.

Therefore, in the first iteration, when the javascript engine sees a var declaration inside of **Block scope**, he leaks its out to the outer scope (in this case: the gloabl scope), so now, the variable, i, will be placed in the global scope!

in the second iteration, when javascript engine will read the 
`var i = 1` inside a **Block scope**, he will want to put it in the global scope again, but this time, we already got a variable named "i" in the global scope. So, as we mention above, in this case of `var`,  "**in `var`, a declaration of variable with a name that is taken by another variable, will just apply the new value to the variable**". So now, the global variable i is equal to 1, and the same thing will happend in the next iteration and it will be with the new value 2. at the end of the last iteration, the for loop will then increase this value by 1 again (to 3), and then the term `i < 3` will be false, and the iteration stream will stop.

```
for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 1000)
}
```

Now, lets talk about the process that will happen second after that. The first timeout will exceed, and the order `console.log(i)` will be executed. But, now the i variable is equal to 3 so the output of this execution will be 3. The same thing will happen in the second timeout that we created in the for loop, and also to the third, which, for summery, will output **"3 3 3"**

So how we can fix that with `let`? Let's see.
```
for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i)
  }, 1000)
}
```

Now, the only change is that the declaration type is `let`, and not `var`. **So how it works?**

Now, every iteration code block looks like this:
```
{
  let i = 0
  setTimeout(() => {
    console.log(i)
  }, 1000)
}
```

In the first iteration, the variable i is declared inside the **Block scope**, and as we study, it will remain there and not leek to the global scope. This occurence will apply also to the secind and third iterations. But there is an inportant fact we need to notice: also the whole three variables i are in **Block scopes** that have a common "level", **each block scope is unique, and has its own environment varibales**. Therefore, the i that exist in the **Block scope** of the first iteration, isnt the same variable that the exist in the second iteration **Block scope**, etc.

its means, that when the console.log(i) execution will occur, each console.log will pring the i that he knows **in his variable environment** and the output will be  "1 2 3"