Introduction
If it was a WAR, 'let' & 'const' would easily crush 'var'. Jokes apart, this article is as detailed as it can get on introducing the declarative statements that exist in JavaScript and clarify the usage on each of them and how they impact the program execution.
ES6 or otherwise known as ES2015 introduced let & const to tackle the issues that was prevalent while using the var declaration.
We'll first look at what declaration vs assignment is and what scope is all about before we get into var, let & const. So, let's get started.
Declaration vs Assignment
Whenever a variable is declared in JavaScript, it is initialised with a default value of 'undefined'. This is referred to as variable declaration.
//Declaration (Not value assigned)
var name;
//JavaScript initializes the variable 'name' with the value of undefined
console.log(name); //Output -> undefined
As you would have guessed, if we assign a value to the variable, it is referred to as assignment.
//Variable declaration & Assignment
var name = 'Skay';
//Variable declaration (no value assigned)
var age;
//Assigning value of 38
age = 38;
//Output -> The name is Skay and the age is 38
console.log(`The name is ${name} and the age is ${age}`);
Now, that we have covered the absolute basics. Let us dive into what 'scope' is.
Scope
You can imagine the scope to be the imaginary boundary where your variables & functions live. If you've declared a variable in a piece of code, the scope determines which parts of code have access to your variable depending on where you have declared your variable.
Don't worry, let us look at what all of the above means with few examples.
In JavaScript, there are three levels of scope -
- Global Scope
- Function Scope
- Block Scope
Global Scope
Global Scope means the variables and functions defined within your JavaScript file is accessible from anywhere.
//Global Scope Example
//////////////////////// FILE 1 - START //////////////////////////////////
//File 1 - The var and functions are defined in the 'employeeAssign.js'
var deptName = 'Finance';
function employeeAssign(name) {
console.log(`Hello ${name}. You have been assigned to the ${deptName} department.`);
}
//////////////////////// FILE 1 - END //////////////////////////////////
//////////////////////// FILE 2- START //////////////////////////////////
//File 2 - The var and functions are defined in a different file called 'employeeGreet.js'
function employeeGreet(name) {
console.log(`Hello ${name}. Welcome to our company.`
}
//Function invoked to greet Employee
employeeGreet('Skay'); //Output -> Hello Skay. Welcome to our company.
//Function invoked to assign Employee
employeeAssign('Skay'); //Output -> Hello Skay. You have been assigned to the Finance department.
//Print the value of deptName
console.log(deptName); //Output -> Finance
//////////////////////// FILE 2 - END //////////////////////////////////
Things to note here:
- The var deptName and function employeeAssign(name) are accessible from other JS files. The only caveat is that the JS file containing the function declarations must be imported into the HTML before the JS File that has the code to invoke those functions.
- Likewise the function employeeGreet(name) is also in Global Scope, i.e., they are accessible globally.
- As you would have guessed keeping everything in global scope is a bad idea, since, others can potentially change the value.
Function Scope
The variables and functions defined within a function are referred to as being inside the function scope.
//Function Scope Example
var name = 'Skay';
function sayHello(name) {
var dept = 'Finance';
console.log(`Hello ${name}. You have been assigned to the ${dept} department.`);
}
sayHello(name); //Output -> Hello Skay. You have been assigned to the Finance department.
console.log(dept); //Uncaught ReferenceError: dept is not defined
Things to note here:
- The variable name is in global scope and is it passed on to the function sayHello(name) which displays the output on console.
- The variable dept is in function scope and hence when it is accessed within the function, it prints fine. However, when you try to access it outside the function, it gives a not defined error.
Block Scope
The variables or functions defined within any block such as an 'if' or a 'for' statement are defined as block scoped.
//Function Scope Example
var name = 'Skay';
function sayHello(name) {
var dept = 'Finance';
console.log(`Hello ${name}. You have been assigned the ${dept} department.`);
if (dept === 'Finance') {
var role = 'Admin User';
console.log(`FROM BLOCK SCOPE: The role is ${role}`); //Output -> FROM BLOCK SCOPE: The role is Admin User
}
//THIS IS OUTSIDE THE BLOCK SCOPE (IF) BUT WE CAN STILL ACCESS THE 'role' VARIABLE
//THIS IS THE PROBLEM WITH USING 'var'
console.log(`FROM FUNCTION SCOPE: The role is ${role}`); //Output -> FROM FUNCTION SCOPE: The role is Admin User
}
sayHello(name);
console.log(`FROM GLOBAL SCOPE: The role is ${role}`); //Uncaught ReferenceError: role is not defined
Things to note here:
- There are 3 scopes here, Global Scope contains the 'name' variable, the function scope contains the 'dept' variable and the block scope (if block) contains the 'role' variable.
- Since the 'role' variable is defined within the 'if' block, it will display the following output 'FROM BLOCK SCOPE: The role is Admin User'.
- Logically, you would expect that the variable 'role' should not be accessible from outside the 'if' block. However, this is the main drawback of using the assignment operator 'var', that the variables inside a block-scope are accessible from outside.
- However, if the variable 'role' is accessed outside the function block, it will output the error, the variable is not defined.
What's the issue with using 'var'?
Let us look at the code below, to understand the real issue using 'var' assignment.
//Add Fruit function takes in a parameter called fruit
function addFruit(fruit) {
//INSIDE FUNCTION SCOPE
//Fruits Array declared within the function scope
var fruits = ['mango', 'banana'];
//Adding the fruit to the array
fruits.push(fruit);
//Print the fruits inside the fruit array
for (var i = 0; i < fruits.length; i++) {
//DANGER - The original fruits array above gets reassigned inside the block scope
var fruits = ['Yellow', 'Green', 'Blue'];
//INSIDE BLOCK SCOPE
console.log(fruits[i]); //Output - Yellow Green Blue (on separate lines)
}
//Display the value of fruits array
console.log(fruits);
//Expected Output -> ["mango", "banana", "papaya"]
//Actual Output -> ["Yellow", "Green", "Blue"]
}
//Invoke the function addFruit()
addFruit('papaya');
In the above code, while fruits array is being re-declared & re-assigned within the for-block, you would assume that it would be a new fruits variable that has a life-span within the for-block.
However, that's not the actual case. It actually overwrites the fruits array defined outside the for loop and when you console.log(fruits) outside the block scope, you would expect to get an output of {'mango', 'banana', 'papaya'} but instead get the output of {'Yellow', 'Green', 'Blue'} displayed on the console.
Let & Const
ES6 introduced let & const as alternatives to var for variable assignment. Let us quickly look at each of them, before going into the details of how they solve the problems that were prevalent with var.
//Variable declaration & assignment of 'name'
let name = 'Skay';
//Variable declaration & assignment of 'age'
const age = 38;
//Output -> The name is Skay
console.log(`The name is ${name}`);
//Output -> The age is 38
console.log(`The age is ${age}`);
//let allows you to reassign the 'name' variable to John
name = 'John';
//Output -> The name is John
console.log(`The name is ${name}`);
//ERROR - Assigning a Constant variable will give an error
//Output -> Uncaught TypeError: Assignment to constant variable.
age = 40;
//Does not execute
console.log(`The age is ${age}`);
The above code is self-explanatory. Simply put, let & const are exactly the same and the thumb rule is that you always use 'const' by default. If you think you'll need to reassign the value to a variable (e.g. counter) then use let.
A quick word on const declaration & assignment with objects. A lot of folks, often get confused by this behavior of const.
//Variable declaration & assignment of person to an Object
const person = {
name: 'Skay',
age: 38
}
//Output -> {name: 'Skay', age: 38}
console.log(person);
//Reassign the attribute 'name' of const peter from Skay to Peter
person.name = 'Peter';
//Reassign the attribute 'age' of const peter from 38 to 40
person.age = 40;
//Output -> {name: 'Peter', age: 40}
console.log(person);
The above code is perfectly valid and while you might think, how can I reassign values to the person object since it is a constant. You should remember, that we are not actually reassigning the value of the person object, rather we are changing the values of the attributes of the person object.
The only time you'll get an error is when you actually try to reassign the value of the person object as shown in the code below.
//Variable declaration & assignment of person to an Object
const person = {
name: 'Skay',
age: 38
}
//Output -> {name: 'Skay', age: 38}
console.log(person);
//ERROR - TypeError: Assignment to a constant variable
//Reassigning value of the const variable person
person = {
name: 'Peter',
age: 40
}
//DOES NOT EXECUTE
console.log(person);
Hope that clarifies things a bit. Now, that we understand let & const, let us find out why they are favoured over var for variable declaration.
So, Why is let & const preferred?
There were two inherent problems introduced by 'var' that is solved by 'let':
- Using 'let' instead of 'var' eliminates the issue related to accessibility with block-scope.
- Using 'let' instead of 'var' also eliminates the issue of accessing a variable before it is declared.
Block Scope Accessibility
I'm going to use the above fruit example and show you how the block scope behavior changes by using let vs var.
Using Var:
//Add Fruit function takes in a parameter called fruit
function addFruit(fruit) {
//INSIDE FUNCTION SCOPE
//Fruits Array declared within the function scope
var fruits = ['mango', 'banana'];
//Adding the fruit to the array
fruits.push(fruit);
//Print the fruits inside the fruit array
for (var i = 0; i < fruits.length; i++) {
//DANGER - The original fruits array above gets reassigned inside the block scope
var fruits = ['Yellow', 'Green', 'Blue'];
//INSIDE BLOCK SCOPE
console.log(fruits[i]); //Output - Yellow Green Blue (on separate lines)
}
//Display the value of fruits array
console.log(fruits);
//Expected Output -> ["mango", "banana", "papaya"]
//Actual Output -> ["Yellow", "Green", "Blue"]
}
//Invoke the function addFruit()
addFruit('papaya');
Using let:
//Add Fruit function takes in a parameter called fruit
function addFruit(fruit) {
//INSIDE FUNCTION SCOPE
//Fruits Array declared within the function scope
let fruits = ['mango', 'banana'];
//Adding the fruit to the array
fruits.push(fruit);
//Print the fruits inside the fruit array
for (var i = 0; i < fruits.length; i++) {
//DANGER - The original fruits array above gets reassigned inside the block scope
let fruits = ['Yellow', 'Green', 'Blue'];
//INSIDE BLOCK SCOPE
console.log(fruits[i]); //Output - Yellow Green Blue (on separate lines)
}
//Display the value of fruits array
console.log(fruits); //Output -> ["mango", "banana", "papaya"]
}
//Invoke the function addFruit()
addFruit('papaya');
As you can see using let, the variables declared within the block-scope are not accessible outside it. Hence, always use let whenever you need to reassign variables.
Accessing a variable before it's declared
In JavaScript, you can do something like this:
//Output the value of the variable name (Note: The name variable has not been declared yet)
console.log(name); //output -> undefined
//Variable Declaration
var name;
You might wonder, that this does not make sense, and using a variable before declaration should ideally give an error. However, due to a concept that exists in JavaScript called Hoisting, all variable & function declarations are move to the top of the stack.
In other words, during execution time, the JavaScript interpreter will convert the above to code to what's shown below.
//Variable Declaration (Variable declarations are moved at the top of the stack)
var name;
//Output the value of the variable name
console.log(name);
If you change the above code to use let, it precisely does what you would have expected in the first time, i.e., throw an error.
//Output the value of the variable name
console.log(name); //output -> Cannot access 'name' before initialization
//Variable Declaration
let name;
I guess that's pretty much it. Now, you know all about var, let & const.
Conclusion
A quick summary of the article -
- By default it is a good practice to use conts.
- Use let when you want to reassign values to a variable. A good example is that of a counter.
- Never use var, since it does not protect from block-scope access.
- let addresses two things that were inherently problematic with var:
- Block-scoped variables cannot be accessed outside the scope.
- Variables cannot be used unless they are declared first.
That's about it folks. I hope you enjoyed this article. Thank you for taking the time to read the article. Do let me know your feedback and comments and don't forget to share it with your friends.
If you enjoyed this, you may also like my other articles:
Top comments (0)