DEV Community

Cover image for var, let and const : ✨demystified✨
Yash Kalaria
Yash Kalaria

Posted on

var, let and const : ✨demystified✨

Hello folks. Welcome to my another post.

In this post, we will learn about var, let and const from the basics to the magical parts of it. So please bear with me till the end.

Variables

  • Variables in programming languages are means to store information in memory and assign a human-readable label to it for future references.
  • There are a few rules for variable naming:
    • The name must contain only letters, digits, or the symbols $ and _
    • The first character must not be a digit
    • It can't contain any whitespace characters
    • There are some reserved words that can't be used as variable names
  • In Javascript, there are two types of variables: Primitives and Reference types. Boolean, string and number are examples of primitive types while objects and arrays are examples of reference type.
  • Javascript is a dynamically typed language. That means we can assign different types to different variables without having an error(for var and let keywords and not const)
  • In Javascript, we can declare a variable using var, let and const.

Some of the terms that I will be using in this post

Scope

  • Scope in Javascript refers to the variable's accessibility in the code. Based on the scope of a variable, some variables can be accessed in some part of the code while some can't be accessed in that part of the code.
  • There are three types of scopes: Global, Function and Block.
    • Variables declared at the top level(outside any function) are global scoped. They can be accessed throughout the program.
    • Variables declared inside a function are function scoped and can only be accessed inside that function. It will throw a reference error if tried to access outside the function.
    • Variables declared inside {} are called block scoped and their accessibility depends on the keyword that was used to declare them(from var, let and const).

Scope chain

  • Javascript creates scopes for every executing function and {} block. There is also a global scope that holds some special values and variables that are in the global scope.
  • Each scope has access to the parent scope in which it is defined. By using it, the current scope can access the variables from the parent scope. This creates a chain of scope which is called a scope chain.

Hoisting

  • JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables or classes to the top of their scope, before execution of the code.

I won't be going deep into any of these topics right now. (Maybe in the future posts 😉)


Now let's learn about var, let and const.

var

  • The var keyword is an old way of creating variables in Javascript.
  • Javascript engine doesn't throw an error if we try to create two variables of the same name in the same scope using var. If the second statement is an assignment then it will replace the value within the variable. If the second statement is just a declaration then it will be ignored. Javascript engine won't throw an error here.
var test = "Hello";
var test;
console.log(test); // Hello
var test = "Nice";
console.log(test); // Nice
Enter fullscreen mode Exit fullscreen mode
  • The scope of a variable declared with var is its current execution context and closures(Maybe in the future post). In simpler words, var declarations are function scoped and accessible inside that function and variables that are declared in the global scope are accessible anywhere.
function testFn() {
  var test1 = "Hello";
  if (true) {
    var test2 = "Nice";
  }
  console.log(test1, test2); // Hello Nice
  function innerFn() {
    var test3 = "Wow";
    console.log(test1, test2); // Hello Nice
  }
  // test3 is not accessible here.
  // It will throw a ReferenceError.
}
testFn();

// test1, test2 and test3 are not accessible here. 
// They will throw a ReferenceError.
Enter fullscreen mode Exit fullscreen mode
  • When using var, we can modify or re-assign any type of primitive values or reference values.
var test = "Hello";
test = "Nice";
console.log(test); // Nice
test = 5;
console.log(test); // 5
test = ["wow"];
console.log(test); // ["wow"]
Enter fullscreen mode Exit fullscreen mode
  • var declarations are hoisted and initialized with the value undefined. What this means is that we can use a variable before it is declared but it won't have any value till any one of the assignment statement gets executed.
console.log(test); // undefined
// It didn't throw an error 🙂
var test = "Hello";
Enter fullscreen mode Exit fullscreen mode

let

  • let keyword is used for creating block scoped variables.
  • Unlike var, we can't have two variable declarations using let with the same name inside the same scope. It will throw an error.
let test = "Hello";
let test = "Bad"; 
// SyntaxError: Identifier 'test' has already been declared
Enter fullscreen mode Exit fullscreen mode
  • The scope of a variable declared with let is the curly brackets containing the variable and for the global scope, it is accessible after the declaration throughout the program.
if (true) {
  let test = "Hello";
  console.log(test); // Hello
}
console.log(test); // ReferenceError: test is not defined
Enter fullscreen mode Exit fullscreen mode
  • let declarations are also hoisted but not initialized. That means accessing a variable before its declaration will throw an error.
console.log(test); // ReferenceError: test is not defined
let test = "Hello";
Enter fullscreen mode Exit fullscreen mode
  • Same as var, when using let, we can modify or re-assign any type of primitive values or reference values.

const

  • let and const are the same. The only difference is in the modification and re-assignment of the variable.
  • All the variables declared using const and having a primitive value can't be modified or re-assigned. It will throw an error if tried to do so.
const test = "Hello";
test = "Wow"; // TypeError: Assignment to constant variable.
Enter fullscreen mode Exit fullscreen mode
  • All the variables declared using const and having a reference type value, can be modified but can't be re-assigned.
const test = ["Hello"];
test.push("World");
console.log(test); // ["Hello", "World"]
test = ["Wow"]; // TypeError: Assignment to constant variable.
Enter fullscreen mode Exit fullscreen mode

Now let's demystify some of the magical cases.

Case 1

  • Let's try to assign a value to a variable before its declaration with let(orconst) and see what happens.
test = "Bad"; 
// ReferenceError: Cannot access 'test' before initialization
let test = "Hello"; 
Enter fullscreen mode Exit fullscreen mode
  • As expected, this gives an error. But a lot is going on here and let's try to understand it.
  • Here test is declared using let, so it will get hoisted, but it won't get initialized. Since it doesn't get initialized, trying to assign it a value will give an error "Cannot access 'test' before initialization".
  • Now let's try to do the same thing with var and see what happens.
console.log(test); // undefined
test = "Wow";
console.log(test); // Wow
let test = "Hello";
console.log(test); // Hello 
Enter fullscreen mode Exit fullscreen mode
  • Here var declaration is first hoisted and then initialized with the undefined value which is why the first console will print undefined.
  • Then as the variable is initialized assigning a value Wow to it works fine and the second console prints Wow.
  • When the Javascript engine comes to the let declaration it simply assigns the value Hello to it and that is why the third console prints Hello.

Case 2

  • Let's see an interesting case with hoisting and variable shadowing.
let test = "Hello";
if (true) {
  let test = "Wow"; // Will this throw an error???
  console.log(test); // Will this execute???
}
console.log(test);
Enter fullscreen mode Exit fullscreen mode
  • Let's try to dissect it.
  • Here we have declared a variable named test and initialized it with the value Hello.
  • Then when it enters the if block, it will create a new scope. As always, Javascript will hoist the declaration of the test variable and it won't get initialized as it is declared using let.
  • Then the Javascript engine will assign it the value Wow. It will work as the let is block scoped and Javascript can have the same named variables in different scopes.
  • Now when we reach the console Javascript engine will try to find the variable in the current scope and as the current scope has the variable with the name test it will use it and it won't use the variable from the parent scope. This is called variable shadowing.
  • As the inner variable's scope is over with if's curly brackets, the last console will print Hello.
  • Let's look at an example with a small variation.
let test = "Hello";
if (true) {
  console.log(test); // 🤔
  let test = "Wow";
  console.log(test);
}
console.log(test);
Enter fullscreen mode Exit fullscreen mode
  • Here when the Javascript engine enters the if block, it will create a new scope. As always Javascript engine will hoist the declaration of the test variable and it won't be initialized as it is declared using let.
  • So as we can guess now there is a variable with an uninitialized state in the current scope so Javascript won't use the parent value and throw ReferenceError: Cannot access 'test' before initialization.
  • Now let's look at the same example using var
var test = "Hello";
if (true) {
  console.log(test); // 🤔
  var test = "Wow";
  console.log(test);
}
console.log(test);
Enter fullscreen mode Exit fullscreen mode
  • Here when the Javascript engine enters the if block, it will create a new scope. As always Javascript will try to hoist the declaration of the test variable but the variables declared using var are not block scoped, they are function scoped.
  • Javascript engine will not hoist it as a same named variable is already there in the current scope. So the first console will use the value from the parent which is Hello.
  • When the engine reaches the declaration of the test variable inside the if block it is treated as the declaration of the same named variable as the var is function scoped and the engine will simply assign the value Wow to the test variable and the second console will print Wow.
  • As the parent variable is re-assigned with the new value, the third console will also print Wow.

Bear with me there is more 😁

Case 3

  • Let's look at an interesting case of var inside the if block.
if (false) {
  var test = "Hello";
}
console.log(test); // Reference error??? 🤔
Enter fullscreen mode Exit fullscreen mode
  • Here as we can see that the if block doesn't get executed as the condition, is false, so it should throw a Reference error. Right? Right???
  • Well here it won't throw a Reference error and instead, it prints undefined 🙂.
  • The reason for this is that the Javascript engine still hoists the test variable even if this code doesn't get executed and our global scope is now polluted with an extra unnecessary variable. One of the reasons why you should avoid using var 😅.
  • In the older code you may see an interesting pattern called IIFE - Immediately Invoked Function Expression through which people avoided the scope pollution.
if (false) { // or true
  (function () {
    var test = "Hello";
    // Some code that uses test
  })(); // Note the invocation here
}
console.log(test); // ReferenceError: test is not defined
Enter fullscreen mode Exit fullscreen mode
  • Here we have created an anonymous function and immediately called it. Javascript treats it as an expression(thus IIFE).
  • As we know that the var is function scoped and thus it can't be accessed outside the anonymous function.

Case 4

  • Let's look at some of the weird cases of the variables declared using var in the case of for loops. Let's start with a simple example.
for (var i = 0; i < 3; i++) {
  // Do something
}
console.log(i); // 3
Enter fullscreen mode Exit fullscreen mode
  • As we can see here that the console prints the value 3 and that is because the variables declared using var are function or global scoped and not block scoped. So here i is accessible even after the for loop. Again scope pollution 🙂.
  • Let's look at another famous for loop problem with var
var fnArray = [];
for (var i = 0; i < 3; i++) {
  fnArray[i] = function () {
    console.log(i);
  };
}
for (var j = 0; j < 3; j++) {
  fnArray[j]();
} // 0, 1 and 2 ??? 🙂
Enter fullscreen mode Exit fullscreen mode
  • Here we may think that it should print 0, 1 and 2 but it won't and let me tell you why.
  • Here we have created an array named fnArray and we have pushed some functions in it which is using the variable i from the for loop.
  • We know that var is function scoped so its accessibility doesn't have to do anything with the for loop. The function is using the variable i but it will only access its value when it is being executed.
  • In the last iteration of the first for loop, i++ will be executed with the value 2 and it will become 3 which will stop the loop. Now variable i will be accessible outside the for loop with value 3.
  • Now when the second for loop gets executed, it will call the anonymous function which will try to console the value of the variable i and as the value of i is now 3 it will print 3 three times.
  • This problem can be solved easily by using let in the first for loop.
var fnArray = [];
for (let i = 0; i < 3; i++) {
  fnArray[i] = function () {
    console.log(i);
  };
}
for (var j = 0; j < 3; j++) {
  fnArray[j]();
} // 0, 1 and 2 as expected
Enter fullscreen mode Exit fullscreen mode
  • This will work because the let variables are block scoped. So each iteration of the for loop will create a scope and it will hold the value of i for that iteration.
  • So when the function will try to access the value of i, it will see the correct value in the scope created by the for loop and print 0, 1 and 2 as expected.

Summary

Summary of var, let and const


So that's it for today folks 😅.
Thanks for bearing with me till the end. Give the post a Heart if you liked the post and give a comment or ping me in case I have missed anything.


You can reach me on:

Latest comments (1)

Collapse
 
l_b_vasoya profile image
lalit-vasoya

Really great article. appreciate your efforts.