If you started working with JavaScript before the release of ES2015, you are probably used to the fact, that JavaScript variables are function-scoped and hoisted etc.. With ES2015, you get a new way to define variables that are block-scoped.
Before ES2015
Back in the days var
was the keyword of choice to define a local variable, if you didn't use var
and used a new indentifier for assignment, you could create a global variable, sometimes even by accident, if you didn't use the "use strict";
statement.
function () {
// Global variable
newIdentifier = 5;
// Local variable
var variable = 6;
// Assignment to the defined variable
variable = 8;
// Another global variable (by accident)
varialbe = 8;
}
Your local variables were scoped by the function in which they were defined.
// You could write something like this:
function (x) {
if(x > 2) {
var y = 10 * x
}
return y
}
// But behind the scenes it would be treated as this:
function (x) {
var y
if(x > 2) {
y = 10 * x
}
return y
}
This lead many developers to define all the local variables at the top of the function, because they would end up there anyway.
Since ES2015
With the release of ES2015 JavaScript got many new features, one of them block scoped variables. There are two kind of them, let
and const
variables.
// So this wouldn't work:
function (x) {
if(x > 2) {
let y = 10 * x
}
return y
}
In the example, y
is only accessible inside the if-block, which is the default behavior of many other languages.
What this allows you to do is define variables where they are needed and scope them with code blocks.
// Every time you see a { and } you're creating a new block
// and in there you can create a new variable scoped to that block
while(...) {
let x = 10;
}
for(...) {
let x = 12;
}
if (...) {
let x = 9;
} else {
let x = 8;
}
try {
let x = 1;
} catch (e) {
let x = 99;
}
You can even use {}
on their own for scoping, to keep the vars as local as possible.
function () {
let varForTheWholeFunction = 10;
{
let scopedVar = getData();
varForTheWholeFunction = parse(scopedVar);
}
// scopedVar isn't accessible here
...
{
let scopedVar = filterData(varForTheWholeFunction);
varForTheWholeFunction = sortData(scopedVar);
}
return varForTheWholeFunction;
}
This can be used for switch
statements too.
function () {
let a;
switch(x) {
case "getData": {
let y = getData();
a = parse(y);
} break;
...
default: {
let y = "defaultData";
a = parse(y);
}
}
return a;
}
So what about const
? Well, their scoping works like with let
, but a value has to be assigned at definition time and it can't change later, only if the function, in which this local variable is declared, is called again.
function (x) {
if (x > 2) {
// This will fail:
const a;
// This will also fail:
const a = 1;
a = 2;
}
}
So it's a variable that can only be set once, but be careful:
function () {
// This would work:
const x = {y: 1};
x.y = 2;
}
Indirections aren't save from mutation.
Conclusion
With let
and const
JavaScript got a way to define scopes more fine granular, which enables developers to limit code dependencies even more.
The cost of this is added complexity to the language, but on the other hand, you don't have to use all of the features that exist :)