Having learned why it's a bad idea to use var in javascript from this extensive article, this article focuses on the two alternatives introduced from es6 and later javascript versions namely let and const. Let and const are the best alternatives to var having articulated all the problems that follow the use of var in javascript projects. This article will articulate when it's the best instance to use let or const in javascript.
1. Let;
Unlike var, let DOES NOT allow redefining of variables within the same scope. This means that unlike in var if a variable is already defined, trying to redefine it a second time will result in an error. Check this article and specifically visit the example of variable redefinition with var to get a better understanding. Basically, this example is NOT allowed by let;
// jshint esversion:6
"use strict";
/* jshint node: true */
let target = 20;
console.log(target);
let target = 'twenty';
console.log(target);
This generates an error indicating that the target has already been defined. It's equally a really bad idea to mix variable declarations with var and let. As this article demonstrates it's best to just forget about the use var altogether.
This in no way should it be confused with variable reassigning which let fully allows and is a legal operation in javascript. Example;
// jshint esversion:6
"use strict";
/* jshint node: true */
let target = 20;
console.log(target);
target = 'twenty';
console.log(target);
This does not redefine the variable but changes its value from 20 to twenty and datatype from a Number to a String.
The only shortcoming of let, which not a huge issue as it doesn't hinder or limit javascript coding occurs when utilizing the browser console from developer tools or the node console when running javascript code in vs-code. Have a look at this article that articulates specifically these methods of running javascript code.
Running code snippets from the two platforms forbid variable redefinition which means multiple variables definitions at a time which as stated isn't a detrimental issue considering emerging platforms like codepen know no such boundaries.
Unlike var, variables declared using let utilize block scope. These variable's usability and availability are limited to the block scope. What's even better, is that these variables are only available after definition avoiding the issue of variable hoisting described in this article.
Using the same example from the var article as follows;
// jshint esversion:6
"use strict";
/* jshint node: true */
console.log(target);
console.log('entering the loop');
for (let i = 0; i < 4; i++) {
console.log(target);
let target = 'target' + i;
}
console.log('Exiting loop');
console.log(target);
Unlike var, let does not allow access to variables before they are defined, and running that code will result in an error that states ;
target was used before it was declared, which is illegal for 'let' variables.**
2. Const;
This keyword is used to define variables whose value shouldn't change. To understand the difference between let and const, there is a concept known as variable mutability.
Basically, A mutable object is an object whose state can be modified/changed after it is created while Immutables are the objects whose state cannot be changed/modified once the object is created.
This is the biggest difference between let and const. From the previous examples, after declaring a variable with let, we can reassign its value and datatype as follows;
// jshint esversion:6
"use strict";
/* jshint node: true */
let target = 20;
console.log(target);
target = 'twenty';
console.log(target);
This means every variable declared using let is mutable or changeable.
Const is a whole new story. A variable declared with const CAN NOT be changed or modified. Basically, this is NOT ALLOWED;
// jshint esversion:6
"use strict";
/* jshint node: true */
const target = 20;
console.log(target);
target = 'twenty';
console.log(target);
Const has a limitation though. To understand it well, Visit this article on differences between primitive and reference values in javascript.
The article elaborates that the major difference between primitive and reference types in javascript is primitive types have a fixed amount of memory space allocated to the values while reference types can be of any size or length.
So back to the limitation, The mutability of the variables declared with const only works on primitive values like Number, String, Boolean, null, undefined, symbol in es6 and the references like objects, functions, and arrays but not the object being referenced. Example;
// jshint esversion:6
"use strict";
/* jshint node: true */
const names = ['Cyrus', 'Codes', 'Hash', 'Node', 'Works'];
console.log(names);
names[1] = 'code';
console.log(names);
From this example, const protects the array, meaning the array can not be redefined but it's content not so much. We can change the array item 'codes' to 'code' and javascript allows it.
More so, operations can be performed in the array like adding new items to the array and removing others, and still, javascript allows it.
// jshint esversion:6
"use strict";
/* jshint node: true */
const names = ['Cyrus', 'Codes', 'Hash', 'Node', 'Works'];
console.log(names); //[ 'Cyrus', 'Codes', 'Hash', 'Node', 'Works' ]
names[1] = 'code';
console.log(names); //[ 'Cyrus', 'code', 'Hash', 'Node', 'Works' ]
//operations
names.push('Blogs');
console.log(names); //[ 'Cyrus', 'code', 'Hash', 'Node', 'Works', 'Blogs' ]
names.pop();
console.log(names); //[ 'Cyrus', 'code', 'Hash', 'Node', 'Works' ]
What isn't allowed is reassigning the array like so;
// jshint esversion:6
"use strict";
/* jshint node: true */
const names = ['Cyrus', 'Codes', 'Hash', 'Node', 'Works'];
console.log(names);
names = [1, 2, 4, 6, 7];
All this means that whilst const prevents the mutability of the object, array, or even functions it does not restrict the same to the internals of these objects.
Even though this is just a caution while using javascript, there is a javascript method that ensures the contents of these objects also remain immutable/ unchanged namely freeze(). Here is an example;
// jshint esversion:6
"use strict";
/* jshint node: true */
const names = ['Cyrus', 'Codes', 'Hash', 'Node', 'Works'];
Object.freeze(names);
names[1] = 'code';
console.log(names);
This will result in an error as follows;
TypeError: Cannot assign to read only property '1' of object '[object Array]'
This partly solved the limitation. Partly? you ask. The freeze method eventually sprouts an identical limitation with const. It only works in the above example because the referred object names[1] is primitive. If this changes to be a reference to another internal object then we run out of luck. This refers to where objects are nested within other objects instead of being a primitive type like the example above.
To elaborate this, let's use an example where an object has an array nested in it as follows;
// jshint esversion:6
"use strict";
/* jshint node: true */
const days = {
total: 7,
color: 'blue',
weekdays: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
};
console.log(days);
days.color = 'red';
console.log(days);
Simply put, there is an object (days) declared as a const making it immutable, which as we have already covered cannot be renamed or basically reassigned.
The object (days) carries in it several variables namely; total, color, and weekdays.
From the example above even though the object is declared using const, its contents are mutable evident when we change the value of color from blue to red.
Still with me? We solve this by introducing the method freeze() making make the object's internals (total, color, and weekdays) immutable as follows;
// jshint esversion:6
"use strict";
/* jshint node: true */
const days = {
total: 7,
color: 'blue',
weekdays: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
};
Object.freeze(days);
console.log(days);
days.color = 'red';
console.log(days);
This code generates an error as follows;
TypeError: Cannot assign to read only property 'color' of object '#<Object>'
This means the method freeze solves the issue of mutability/changeability of our internal making it unchangeable or immutable which is the solution to the limitation of const. From the same example, the object days has an internally named weekday which is essentially an array.
Believe it or not, this is where the power of the freeze method comes to an end. Remember that freeze() works on primitives and objects but not the internals of NESTED objects.
This means, while it's impossible to change the color (which is a primitive value), once the freeze is applied elaborated by the example above, it's very possible to change any of the internals of the nested array (weekdays) to whatever data possible as follows;
Change first internal of the nested array in this case sun to Sunday;
// jshint esversion:6
"use strict";
/* jshint node: true */
const days = {
total: 7,
color: 'blue',
weekdays: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
};
Object.freeze(days);
console.log(days);
days.weekdays[0] = 'Sunday';
console.log(days);
It all becomes very complicated from here where javascript freely allows continuous nesting of variables and object making them mutable. Example;
// jshint esversion:6
"use strict";
/* jshint node: true */
const days = {
total: 7,
color: 'blue',
weekdays: ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat']
};
Object.freeze(days);
console.log(days);
days.weekdays[0] = ["Sunday Morning", "Sunday Afternoon"];
console.log(days);
In Summary;
Var;
- Var allows variable redefinition which is a bad idea for various reasons elaborated in this article..
- var allows variable hoisting - Acess and usability of a variable before the declaration.
- NO block scope - Variables defined within a block example in a loop, are accessible outside this scope.
Let
- Does NOT allow variable redefinition.
- Does NOT allow variable hoisting- access, and usability of variables before the declaration.
- Allows variable reassignment either the value or even the datatype (Mutable/Changeable). Has
- HAS block scope - Variables defined within a block example in a loop are accessible only inside this scope.
Const
- Does NOT allow variable redefinition.
- Does NOT allow variable hoisting- access, and usability of variables before the declaration.
- Does NOT allow variable reassignment either the value or even the datatype (NOT Mutable / NOT Changeable).
- HAS block scope - Variables defined within a block example in a loop are accessible only inside this scope.
freeze() method
- Makes primitive and objects declared with const immutable/unchangeable which is evident where it's impossible to change the color of the object from blue to red once freeze() was introduced.
- Does NOT make the internals of nested objects immutable/unchangeable which is evident where the values of the nested array are changed in one example from a primitive String(sun) to another primitive String (Sunday), and the second example from a primitive String (sun) to a nested array with two primitive Strings namely Sunday Morning and Sunday Afternoon.
Having covered extensively the usability of const and let, it's imperative to understand the benefits of using const over let which include;
Fewer errors in javascript code,
Easy management and flow of logic of javascript code,
Const prevents unintentional changes in variables intended to be an immutable example;
const pi = 3.142;
The freeze () method solves the problem where both let and const are powerless which is the mutability of their internals. This means they both allow referred values by objects to be changed as evident in the following two examples;
// jshint esversion:6
"use strict";
/* jshint node: true */
let days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
console.log(days);
days[0] = 1;
console.log(days);
// jshint esversion:6
"use strict";
/* jshint node: true */
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
console.log(days);
days[0] = 1;
console.log(days);
Which is solved as follows;
// jshint esversion:6
"use strict";
/* jshint node: true */
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
const days = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
console.log(days);
Object.freeze(days);
days[0] = 1;
console.log(days);
Throughout the code, the following lines have been included specifically at the top.
// jshint esversion:6
"use strict";
/* jshint node: true */
They help run our code in strict mode. To know what exactly this means, read this article.
Personal Note: This article has been a long one but very informative. Frankly, I've learned and understood more than I had when I started it about the variable declaration, nesting of objects, and limitations of every variable declaration keyword namely var, let, and const.
To understand everything in it my best advice would be to run all code snippets attached and have a look at its outcome, try to reason it out, and then check the preceding explanation for clarification.
To easily run all your javascript code snippets, read this article and choose your best option to run these snippets.
For more of these and other related articles, please follow this blog or proceed to follow me on Twitter, and more will keep flowing your way.