For a long time now, declaring variables using the standard var keyword was commonplace in JavaScript. While super easy to use, it was not without its limitations. For one, it created an issue with 'hoisting'. Any variable declared within a function, or within the global score, was 'hoisted' up to the top of function (or global scope). Which meant that variables within smaller code-blocks, such as if-else statements or for loops, were not actually local to those blocks.
function func1()
{
for (let i = 0; i < 10; i++)
{
var tmp = 10;
}
console.log(tmp); // still works
}
Take this as an example. In this case, the tmp variable is actually still available to be used outside of the for loop, even if that was not an intent that we had in mind. This was something that we normally just had to deal with as developers in the past.
Block level declarations
The new ES6 specification allows for block-level variable declarations by introducing the new 'let' and 'const' identifiers. You might also see block scopes also referred to as 'lexical scopes'.
These block-level scopes can be created either inside of a function, or inside of any code block, such as with an if-else statement or for loop.
'let' declarations
The let declaration gives us true 'block-level' scope for our variables for the first time.
function func1()
{
for (let i = 0; i < 10; i++)
{
let tmp = 10;
}
console.log(tmp); // would result in an error
}
The tmp variable in this case only belongs to the for loop and can only be used within that scope. This behavior is more what we would actually expect it to behave like in other programming languages.
Note: This does not mean go out and replace every single var that you see with a let. Most of the internet was probably written during a time before the ES6 specification, which means some code will be structured to work with var's 'hoisting' capabilities in mind. Changing this behavior could lead to unexpected behavior, such as you breaking production.
'const' declarations
We also now have the 'const' declaration to further restrict how variables are treated and modified. With the 'const' declaration, variables can not be modified once they have been initialized. This is ideal for static values, such as Pi or for any other constants that you know for a fact can not be changed.
Note that you have to initialize a 'const' variable with some form of value. Not doing so will throw an error.
const pi = 3.14;
pi = 5; // error
Constant variables are treated as 'block-level' variables similar to variables declared using 'let'. Some would argue that every variable should be declared as a 'const' and only changed otherwise if the code requires it to be so. In this case you reduce the chance of unexpected variable updates. There could definitely be something to that approach.
Objects and 'const'
There is one thing to make note of when using the 'const' declaration with JavaScript objects. While you cannot modify the variable object itself, you can modify the objects properties.
const car = {
make: 'Honda',
model: 'Fit'
};
car.year = '2018'; // this is valid
Note that this includes adding new properties to the object as well.
Loops and block-level declarations
Many developers never realized that for-loop declarations were one of the areas that required block-level variable declarations the most. Take the following for example:
for (var i = 0; i < 10; i++)
{
// code goes here
}
console.log(i); // still works!
In this case, the 'i' variable is still accessible outside of the for loop! Which pollutes our variable namespace in the long term. This could also have issues if you had multiple for loops in a given function, in which case you would end up re-declaring the 'i' variable, which might not be the intended effect.
for (let i = 0; i < 10; i++)
{
}
console.log(i); // error!
Loops and 'const' declarations
While you are free to declare a 'const' inside of a for loop initializer, if the variable is at all modified, it will throw an error.
for (const i = 0; i < 5; i++)
{
// will only run one time before error
}
If however, you choose a function that does not modify the initializer, such as a for-in loop, then you are just fine.
var car = {
make: 'Honda',
model: 'Fit'
};
for (const key in car){
console.log(key); // totally fine
}
Summary
Block-bindings allow for cleaner and more scalable code and is the default behavior for many of today's programming languages. It's a great addition to the JavaScript language, and while the adoption rate is a slow process, the best time to start using these concepts is today.
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.