ES6 cool stuffs —var, let and const in depth

A way to explain using Groot. Character credit goes to Marvel 😄

In this article, we will continue exploring some of ES6 cool stuffs — let, const statement against our classical var statement. It won’t be long, I promise.

Let’s begin!


JavaScript variables are containers for storing data values

How do you normally create new variable in Javascript? OK, i know it’s a silly question since it’s too basic to ask — we all use the “var” statement.

Var

var firstVar; //declare firstVar width default value - undefined
var secondVar = 2; //declare and assign 2 to secondVar variable.

Just in case someone is wondering, var stands for “variable” in English (obviously 😆).

Like many modern languages, JavaScript provides us such flexibility that there is no need to decide on a specific type for our variable when we declare them.

Integer? String? Object? Function? … —one var fits all.

Life is easy, until you encounter something like this:

var increment = 1;
if (increment === 1){
var increment; //re-declare increment with default value
//Do something
}
console.log(increment); //What will be printed here?

What is going on here? First of all, we will need to mention about hoisting.

A closer look — Hoisting

Hoisting, refers to the default behavior of Javascript to process and put all variables and functions declarations into memory first during compile phase of its execution context, regardless where they are written in code.

In more common explanation, it is the JS’s behavior of moving all the declarations to the top of current scope — not physically, aka no change to your code at all — and will be processed before everything else.

Take the example above, during the compile phase the code will be understood as

var increment;
var increment;
increment = 1;
if (increment === 1){
...
}
console.log(increment); //print 1

Or in another example:

var x = 0;
y = 1;
console.log(sumOf(x,y));
var y;
function sumOf(a, b){ return a + b; }

It will be processed as:

var x;
var y;
function sumOf(a, b){ return a + b; }
x = 0;
y = 1;
console.log(sumOf(x,y));

Note here JavaScript hoisting only applies to declarations, not assignments. All value assignments will be processed at the same location where they are written/located in the code, resulting in:

console.log(x); 
x = 3;
var x = 1;
console.log(x);

will print out undefined, 1 and NOT 1, 3 as some may thought.

In addition, we mentioned above about execution context, which is — according to JavaScript documentation — the scope of a declared var variable. So what about it and how does it affect var variable declaration & assignment?

What is execution context?

Execution context is the environment where Javascript code is executed/evaluated. It can be either:

  • Global — default environment
  • Functional — environment inside a function.
  • Eval — environment inside eval function.

Hence here the scope of a var variable is either within enclosing function or global context.

Any other block context — means block of code inside {} curly braces , code statement, expression, etc…—which is not from 3 defined types above will not affect the mentioned variable’s scope.

As a result, declaring a variable in a block of code statement, expression— if…else … statement, for loop, etc — will be understood as declaring it at the top in the compiler/creation phase.

function testMe(){
while(true){
var x = 2;
break;
}
   console.log(x); 
//Still print 2 even though x is declared inside while loop
}

And so, that’s var. As we can see, this statement provides all the flexibility needed for developers (But please, don’t overuse it!). So why do ES6 bother to introduce 2 more statements — let and const ? Let’s find out.

Let

let, similar to var, declares a variable and allows (optionally) to assign any value to it. However, unlike var, it declares variable only as block scope local one.

It means the variable will be declared, existed and limited to use only inside block ({}), statement or expression and also available to its sub-block beside normal execution context (enclosing function, etc).

Therefore, in the above example, while x is not limited within while block scope like most language will do, if we replace var by let, the result will change:

function testMe(){
while(true){
let x = 2;
break;
}
   console.log(x); //ReferenceError: x is not defined
}

Yup, not undefined, not null, not 2 but a ReferenceError instead.

This ensures the locality of the variable, and we can rest assured that we won’t change by mistake any variable with same name declared somewhere before. Like in this situation:

var x = 1;
{
let x = 3;
}
console.log(x); //Still 1

Yippie, less chance for 🐛 to go un-noticeable!

But wait, there is more. Since it is limited by block scope, we finally can implement private members without the help of closure.

var Person;
{
let name;
Person = function(_name){
name = _name;
};
Person.prototype.getName = () => name;
}
var person = new Person('Maya');
console.log(name); //Nothing is printed
console.log(person.getName()); //Maya

Another important difference from var is that variable hoisting doesn’t apply to let, which means during compile phase, let declaration will stay where it is and will not be processed first among other code — aka will not move to the top of context like var. Thus in executing this example,

x = 5;
y = 2;
let y; 
var x;

it will yield ReferenceError again for y, but not for x.

Finally, let doesn’t create a property on global object, unlike var when being used in the global context. So no messing around with global object by accident!!! 🚀

var x = 5;
let y = 4;
console.log(this.x); //5
console.log(window.x); //5
console.log(this.y); //undefined
console.log(window.y); //undefined

And unlike var, re-declaring a let variable will throw SyntaxError.

SyntaxError for x

So one thing for certain, when using letwe will have more restrictions/limitations (for the better I must say) than the normal way of var declaration. What about const?

Const

Let’s say, you have some data variables like templates, default messages, etc… They are meant to be used as constants — without changing. How will you make sure these data will stay unchanged throughout application using JavaScript?

Before ES6, one way — be careful what you write (or pray that other developer also notice and understand the naming conversion you use for constant variables).

After ES6 (thank God) we have const

const — same as let allows to declare and initialize a local block-scoped variable. Thus, it has all the restrictions of let such as:

  • Declared variables are only available to use in inside block {} of code, statement, expressions beside the normal execution context.
  • No variable hoisting applies to const
  • No property created in global object when it is used in global context.
  • Declared variable can’t be re-declared.

In addition, as you can guess, const — stands for constants:

  • Declared variable has to be initialized with a value.
const myConstants; //SyntaxError: Missing initializer in const declaration
  • Declared variable can ONLY be assigned with a value ONCE. No re-assignment — as expected for a constant value.

But one downside is that if the assigned value itself is in a form of an object (object, array, etc), it still can be modified. For example:

const myConstant = {name: "Constant"};
myConstant = {name: "new Constant"}; //Error
myConstant.name = "new Constant"; //OK
console.log(myConstant.name); //new Constant
const arr = [1, 2];
arr = [2,3]; //Error
arr[0] = 2; //OK
console.log(arr); //[2,2]

Clear and easy to understand, isn’t it?

In general, you probably notice the benefits these new statements bring to us by now, hence let’s sum it up.

Advantages of let and const

  • Avoid polluting our global object with unnecessary properties.
  • Avoid hidden 🐛 — such as modifying a constant value by mistake, updating wrong variables which are in different scope block but declared with same name, etc…
  • Avoid unnecessary hoisting.
  • Add more restrictions to force our code more reliable, organized and easier to read (how am I supposed to know if a variable is supposed to be const if it is declared as var ??).

Conclusion

Actually no matter how much advantages let and const can have against var, they are not meant to replace var but more as additional ways to provide more constraint functionalities to JavaScript and help developers saving time in code reviewing and reading.

My piece of advice, as always, is to analyze your need first and only then choose the statement accordingly, even if it’s just a small variable — remember, a lot of small and seem-to-be unimportant things can lead to a big scary nightmare someday if they are not handled with care in the beginning😉.

After all, who doesn’t like writing clean and safe code? 😃