These
This is article 1 of the series. It is about let and const. Two new ways to declare a variable.
- ES6 Part 1 – Let & Const
- ES6 Part 2 – Template Strings
- ES6 Part 3 – Arrows
- ES6 Part 4 – Default Parameters
- ES6 Part 5 – Object Additions
- ES6 Part 6 – Rest and Spread
Var, let, and const
var used to be the only keyword in JavaScript to declare a variable:
var sVariable = “I’m a var declared variable.”;.
let and const are the two new keywords to declare a variable:
let sVariable = “I’m a variable declared by let.”;.
const sVariable = “I’m a variable declared by const.”;.
But what are the differences
Hoisting
In order to understand the differences between var, let, and const, it is a prerequisite to know what Hoisting is in JavaScript.
Hoisting means that JavaScript puts behind the scenes the declarations of all your variables to the top of the scopes.
And a variable delaration is var bVariable;, let bVariable;, or const bVariable;. A variable initialization is bVariable = true;. Hence a variable declaration and initialization is var bVariable = true;, let bVariable = true, or const bVariable = true.
The scope of a variable is the space around a variable from which you are able to access the variable. For example, to access a variable to initialize this variable with a new value like bVariable = false;.
Two scopes exist: the global scope and the local scope.
Global scope: A variable is in the global scope if you declare it not in a function. A variable in the global scope is accessible from everywhere.
Local scope: A variable is in a local scope if you declare it in a function. A variable in the local scope is to access only from within the function.
And there is one global scope. But there can be multiple local scopes.
// global scope var sVariable1 = "I'm in the global scope."; console.log(sVariable1); // returns I'm in the global scope. // local scope function myFunction { // here starts the local scope var sVariable2 = "I'm in the local scope of myFunction."; console.log(sVariable2); // returns I'm in the local scope of myFunction. } // here ends the local scope console.log(sVariable2); // returns null
Therefore, if your JavaScript file looks like this …
function fnFuntion(bVariable1) { // scope starts if (bVariable1) { var bVariable2 = true; // declare and initialize variable console.log(bVariable1); } } // scope ends
… then behind the scenes, it the same function looks like this due to the so called hoisting.
function fnFuntion(bVariable1) { // scope of function starts var bVariable2; // declaration of bVariable2 got hoisted if (bVariable1) { bVariable2 = true; // declaration of bVariable gets hoisted to the top of its scope console.log(bVariable1); } } // scope of function ends
Before runtime and behind the scenes the bVariable2 was put to the top of its scope. The scope of bVariable2 is a local scope. It is the function fnFunction(). Because bVariable2 sits within fnFunction(). As explained above.
To sum up, hoisting means that a mechanism moves behind the scenes before runtime all of your JavaScript declarations to the top of their respective scopes.
And if you would not know about hoisting then you would be surprised about by the following code samples.
(Digression: An Undefined is thrown when a variable is not assigned to a value. If a variable is not initialized. A ReferenceError is thrown when it is tried to use a variable which does not exist. If a variable is not declared.)
function fnFunction(bBoolean) { // fnFunction is the local scope of bVariable2 if (bBoolean) { var bVariable2 = false; // declare and initialize bVariable2 console.log(bVariable2); // console output is as expected false } } fnFunction(true);
// maybe still no surprise - first console output is false and the second is an Undefined function fnFunction(bBoolean) { // fnFunction is the local scope of bVariable2 if (bBoolean) { var bVariable2 = false; // declare and initialize bVariable2 console.log(bVariable2); // console output is as expected false } else { console.log(bVariable2); // console output is an Undefined } } fnFunction(true); fnFunction(false);
// but at the latest here comes the surprise - first console output is false. But the second output is a ReferenceError and not an Undefined as in the first code sample function fnFunction(bBoolean) { // fnFunction is the local scope of bVariable2 if (bBoolean) { var bVariable2 = false; // declare and initialize bVariable2 console.log(bVariable2); // console output is as expected false } else { console.log(bVariable100); // console output is a ReferenceError } } fnFunction(true); fnFunction(false);
But now that you know about hositing you know what happens behind the scenes. Because behind the scenes it looks like this:
// behind the scenes declarations get moved to the top of their scopes function fnFunction(bBoolean) { // fnFunction is the local scope of bVariable2 var bVariable2; // declaration of bVariable2 gets moved to the top behind the scenes of its scope if (bBoolean) { bVariable2 = false; // initialize bVariable2 console.log(bVariable2); // console output is as expected false } else { console.log(bVariale2); // console output is an Undefined console.log(bVariable100); // console output is a ReferenceError } } fnFunction(true); fnFunction(false);
Let
Now that hoisting is understood the new declaration keywords let and const are self -explanatory. If you should have not understood hoisting yet please read again the section above about hoisting.
If you use let instead of var in the second code sample from above then this happens:
function fnFunction(bBoolean) { // fnFunction is the local scope of bVariable2 if (bBoolean) { // var bVariable2 = false; // we do not use var as before let bVariable2 = false; // now we use let instead of var console.log(bVariable2); // console output is as expected false } else { // console.log(bVariable2); // Undefined was the error if used var console.log(bVariable2); // now the console output is a ReferenceError } } fnFunction(true); fnFunction(false);
As you see: No more hoisting. This means if a variable is declared through let then it does not get hoisted. If the variable is in
And a block scope is just another scope. Like the further
But the block scope refers not just to functions. But to all kind of blocks. For example, an if or for
if (true) { // here starts the block scope // } // here ends the block scope for (let i = 0; 0 < 10; i++) { // here starts the block scope // } // here ends the block scope
And if you declare a let variable inside a block scope then this variable will not be hoisted.
if (true) { // here starts the block scope let sVariable = "string"; } // here ends the block scope console.log(sVariable); // ReferenceError and not an Undefined
For the sake of
if (true) { // here starts the block scope var sVariable = "string"; } // here ends the block scope console.log(sVariable); // Undefined and no ReferenceError
… and this looks behind the scences like this:
var sVariable; if (true) { // here starts the block scope sVariable = "string"; } // here ends the block scope console.log(sVariable); // Undefined and no ReferenceError
Const
The const keyword has the same scope features as the let keyword. But on top of that the const declaration has a bonus:
If a variable is declared as const then it is literally a constant. This variable is immutable:
var sVariableVar = "foo"; sVariableVar = "bar"; // possible let sVariableLet = "foo"; sVariableLet = "bar"; // possible const sVariableConst = "foo"; sVariableConst = "bar"; // Run Time Error: TypeError
This means it is not possible to assign a new value to
But that does not mean, that it is not possible to mutable the object which a const variable holds:
const oVariable = { property: "value" }; oVariable.property = "another value"; // possible const aVariable = ["value"]; // an array is an object too aVariable.push("another value"); // possible
When to use var, let, or const
This is a matter of taste. There are currently two main approaches.
The first approach is to use const as default. And to use let whenever there is the need to assign a new value to a variable. Plus, not to use var at all anymore. Event for variables which are supposed to be on the top global level.
const iVariable = 1; // const as default let sVariable = "foo"; // let when the variable gets later a new value assigned sVariable = "bar";
The second approach is to use let as default. And to use const whenever a variable should later not be assigned with a new value. Plus, not to use var at all anymore as well.
let sVariable = "foo"; // let as default sVariable = "bar"; const iVariable = 1; // const when the reference to the variable should not be changed
I prefer the second way. Here is why: First of all, by using let as default there are every time two characters less to type. Plus, the two characters less make the code more clean.
And secondly, if the const keyword is only used for variables which indeed are supposed to be constant than this leaves a kind of a message to others who read your code.
Even if a const variable is mutable if it is an object. The const keyword is a signal to others that with the reference of this variable is not to be messed with. Obviously, it is not to be reassigned. Because you declared this variable const on purpose and not by default. Furthermore, if the object references an object then the object should not be mutated.
Anyway, no more use for var.