Introduction
In this article, I would like to introduce the three declarative statements 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 address an issue that was prevalent with the var declaration. We will look at that in this article.
But, before that, I would like to begin with the absolute basics first. I would like to first touch up 'What is declaration vs assignment?' and then introduce scope and finally we will look at var, let & const.
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 initialises 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 they have to be imported into the HTML before the invoking function of the JS file.
- 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 an 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 which contains the 'name' variable, the function scope which contains the 'dept' variable and the block scope (if block) which contains the 'role' variable.
- Since the 'role' variable is defined within the 'if' block, it displays the output 'FROM BLOCK SCOPE: The role is Admin User'.
- Logically, you would expect that the variable 'role' should not have been accessible outside the if block. However, this is the main drawback of using the assignment operator 'var', that it is accessible outside the block scope.
- 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 might 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, use 'const' and only change it to 'let' if there is a need of a variable reassignment.
- A good example of when to use 'let' is 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.