The Difference Between var, let and const in JavaScript: Part 2
Back to the basics with variables and scope
In a previous post I discussed some of the differences between var
, let
and const
.
This post goes a little deeper into differences between the three. So hold on to your consoles!
The Differences Between let and var
A var
variable can be redeclared and updated.
A let
variable be be updated but not redeclared.
An example of trying to redeclare a let variable:// In editor:<script>
let points = 50;
let points = 60;
</script>// In the console I get an error:
Uncaught SyntaxError: Identifier 'points' has already been declared
However, I can update it:
let points = 50;
points = 60;// In console:
points// Returns:
60
The Scope of let
If I declare a let
variable at the global scope, then redeclare it within a block (curly brackets) as in the example below, I will not get an error in the console, but it will not actually redeclare let
.
let points = 50;
let winner = false;if(points > 40) {
let winner = true;
}// If I call:
winner// It returns the first value:
false
Why is this happening?
Because let winner = false
and let winner = true
, are actually two separate variables because they are scoped differently, even though they have the same name. To clarify:
// This 'let' is scoped to the window (globally):
let winner = false;if(points > 40) {
// This 'let' is scoped to the block (between the curly brackets):
let winner = true;
}
In the above example if we change bothlet
variables to var
, then call winner
in the console it returns true
because it is not inside a function. (Remember: var is function scoped.) The var
variable is being redeclared within its scope, which is the window in this case.
let points = 50;
var winner = false;if(points > 40) {
var winner = true;
}// If I call:
winner// It now returns:
true
The Differences Between let and const
const
variables cannot be updated. let
variables are made to be updated.
// If I define the const variable:
const key = 'xyz123';// Then try to redeclare it:
key = 'xyz1234'// I get the following error:
Uncaught TypeError: Assignment to constant variable.
There is an interesting caveat to this, though. If I create a const
variable that is an object, the attributes of that object can be updated.
// Creating my person object:
const person = {
name: 'Joseph',
age: 33
}// Calling person in the console:
person// It returns:
{name: "Joseph", age: 33}// If I then redeclare the age attribute:
person.age = 34// When I call it:
person// It returns:
{name: "Joseph", age: 34}
Note: If I want to make object unchangeable I could “freeze” it:
const joseph = Object.freeze(person);
If I then try to change an attribute of the person
object in my console, it does not take the changes:
// If I call:
person// It returns:
{name: "Joseph", age: 33}// However, if the object has been 'frozen' and I try to change the age attribute:
person.age = 34// It will immediately return:
34// But then when I call it:
person.age// It returns the original age:
33
The advantages of using let vs. var in a for loop
I’ve run into many issues regarding scope with for loops when defining i
with var
. This is where using let
could prove advantageous.
// Running a for loop with var:
for(var i = 0; i < 10; i++){
console.log(i);
}// Returns 0 through 9 in the console, but if I call:
i// It returns:
10
In other words, i
has leaked out of the scope of the for loop, and can now be called at the global scope (or whatever the parent scope is)!
The other issue with using var
in a for loop is when you want to do something within the scope of the loop, but after the loop runs. i
is being redeclared each time the loop runs.
// Do something one second after the loop runs:
for(var i = 0; i < 10; i++) {
console.log(i);
setTimeout(function() {
console.log('The number is ' + i);
}, 1000);
}// If we then call i in the console it returns:
10
Again, the variable i
is being overwritten every time the loop runs. By the time setTimeout
runs, i
is 10.
A quick way to fix this is the change var
to let
. Because let
is block scoped (within the curly brackets), it will behave differently, and will not reassign i
every time the loop runs:
for(let i = 0; i < 10; i++) {
console.log(i);
setTimeout(function() {
console.log('The number is ' + i);
}, 1000);
}// This returns 0 through 9 in the console, then after one second logs:
The number is 0
The number is 1
The number is 2
The number is 3
The number is 4
The number is 5
The number is 6
The number is 7
The number is 8
The number is 9