Photo by Andrew Neel on Unsplash
Mastering JavaScript Variables: Understanding the Difference between let, const, and var
Since ES6, we got an addition of the let and const keywords. But why was there a need for them in the first place?
And since everyone around you is suggesting that let is the new var, should we stop using var altogether?
After understanding the differences and seeing the flaws of var should we replace let with var in all our older programs?
Let’s explore all of these questions in depth in this article.
What is global/function scope VS what is block scope
The variables that are declared globally are global scope. Similarly, the ones declared within a function are function scope. The main thing to remember here is global variables are accessible by the whole program and the variables with function scope can be accessed within that function only.
Block scope in simple terms means anything that was declared within the opening braces and the closing braces. Yup, and those variables can’t be accessed outside of that block.
Now, keeping this in mind let’s move forward.
var
The scope of var is global / function type.
Here are some important points to keep in mind when using var.
You can technically use a variable before it is declared. It’ll just return undefined. There are no errors.
function foo(){ console.log(a); //undefined a = 'foo'; console.log(a); //foo, Note - here the variable is defined later but still the value can be assigned var a = 'bar'; console.log(a); //bar } console.log(a) //Throws an error - a is not defined!! //Since 'a' was defined inside of a function and //thus, has a function scope that does not allow //it to be used outside of the function foo();
You can redeclare a variable within the same scope. P.s. I feel this is the weirdest thing acceptable in JS syntax.
function foo(){ var a = 'baz'; console.log(a); //baz a = 'foo'; console.log(a); //foo var a = 'bar'; console.log(a); //bar } foo();
We also need to understand why let was introduced hence study the below example :
function foo(){ for(var i = 0; i < 5 ;i++) { console.log(i); // prints 0 through 4 } console.log(i); // print 5! Why? //Here, the declaration of i //technically works the same way //as if we have declared it in the first line of function. //Hence, i is still available to us for use } foo();
Now this case does not affect us that much in this function. And even if we decide to write another for loop within the same scope, since JS allows variables with the var keyword to be redeclared it does not create bugs for us. But there are many real-world scenarios where we could not allow our variable to be overwritten by some other value and this behavior of var might lead to buggy code.
Armed with this knowledge we are ready to welcome let in our JS vocabulary.
let
The last example that we saw above clearly shows that even after using var in the block scope (loop), the value was available for use outside of the block. If we replace var with let, we see that now the block scope is clearly defined.
function foo(){
for(let i = 0; i < 5 ;i++)
{
console.log(i);
// prints 0 through 4
}
console.log(i);
//immediately gives us an error - i is not defined
}
foo();
Another example. The below code is written with a var declaration inside of the if block.
function foo(){
var a = 'baz';
console.log(a); //baz
if(a==='baz'){
var b = 'bar'
console.log(b) //bar
}
console.log(b) //bar
}
foo();
This code is essentially the same as if we would have declared variables in the function scope. But still, we internally know that we want ‘b’ to be within the if block.
function foo(){
var a = 'baz';
var b = 'bar'
console.log(a);
if(a==='baz'){
console.log(b)
}
console.log(b)
}
foo();
If you are used to Java or C#, this would be engraved inside your head. But JS has this exception. And with the release of ES6, we were given a solution for this type of exception.
Now let us convert the above code in terms of let
function foo(){
var a = 'baz';
console.log(a); //baz
if(a==='baz'){
let b = 'bar'
console.log(b) //bar
}
console.log(b) //b is not defined
}
foo();
Yay! Now we have successfully enforced block scoping.
Variables can’t be redeclared inside the same scope when you define them using let. So we even overcame the issue of redeclaring variables we faced while using var.
function foo(){
let a = 'baz';
console.log(a);
let a = 'bar';
console.log(a);
}
foo(); //SyntaxError: Identifier 'a' has already been declared
Though, we can reassign values.
function foo(){
let a = 'baz';
console.log(a); //baz
a = 'bar';
console.log(a); //bar
}
foo();
Redeclaring a variable is also possible inside different scope
function foo(){
let a = 'baz';
console.log(a); //baz
if(a==='baz'){
let a = 'bar';
console.log(a); //bar
}
console.log(a) //baz
}
foo();
const
The values of const once declared cannot be modified. Like let, it is block scoped.
But the below nuances should be kept in mind :
When using the push method for an array defined with the const keyword, it allows us to modify the array.
const numbers = [1,3,5,7];
numbers.push(9)
console.log(numbers);
// [ 1, 3, 5, 7, 9 ]
numbers = [ 1, 3, 5, 7, 9 ]
// TypeError: Assignment to constant variable.
Why did it allow us to modify the array using the push method? It’s because arrays are stored by reference in JS. And by pushing a number in the array you are not modifying the reference of the array. But when we try reassigning value by using an equal sign, it defies the purpose of const and throws a type error.
We can’t redeclare a const.
Const always needs to be initialized.
const numbers;
// SyntaxError: Missing initializer in const declaration
For objects: you cannot reassign values
const details = {
firstName : 'Mariya',
lastName : 'Zaveri'
};
details = {
firstName : 'Mariya',
lastName : 'Zaveri',
gender : 'Female'
};
//TypeError: Assignment to constant variable.
But the keys are not protected. Hence, it is possible to add keys to the object
const details = {
firstName : 'Mariya',
lastName : 'Zaveri'
};
details.gender = 'Female'
console.log(details);
//{ firstName: 'Mariya', lastName: 'Zaveri', gender: 'Female' }
Bonus tip: To make the object immutable you can use Object.freeze() method. The same can be used for arrays
"use strict"
const details = {
firstName : 'Mariya',
lastName : 'Zaveri'
};
Object.freeze(details);
//throws an error in strict mode.
details.gender = 'Female'
//TypeError: Cannot add property gender, object is not extensible
console.log(details);
Is let the new var?
But why was there a need for let and var in the first place?
Let was introduced for its block-scoping capabilities. While const was for retaining values that are constant.
Storytime: I come from a Java background. So for me, block scope is the default scope. For JS, function scope is the default.
After understanding the differences and seeing the flaws of var should we replace let with var in all our older programs?
To answer this technically we can have let in place of var since it gives us the block scoping.
But should we? In his article, Kyle Simpson argues that it is better if we use var and let as companions instead of running behind the cult let is the new var. Using var for function scopes and let for block scope clearly identifies what the scope of the given code is. So we must use both.
After understanding the differences and seeing the flaws of var should we replace let with var in all our older programs?
NO. Since let is block scope we can’t blindly change vars in our program. There will be logic that might stop the execution flow of the program.
Summary
Use let and const whenever you require block scope.
Use var when you want a function scope.