Today we are going to discuss an interesting topic of EcmaScript6(ES6), Block Bindings. This discussion is going to be about bindings variables to scope.
Binding?
Binding is the formal name of variables. When we declare and/or initialize a variable, we actually bind a value to a name inside a scope. And scope means to a specific part of a program.
So we bind a variable whenever we declare and/or initialize a variable using one of these keywords: var, let and const in JavaScript.
Example:
var a
let b
const last = c
Hoisting
In short, hoisting is a mechanism that handles execution contexts in JavaScript. This means the variable and function declarations (not initializations) are put into the memory during the compile phases before going for any execution.
For example:
function getName(condition) {
var name
if (condition) {
name = 'Sherlock'
console.log(name)
}
else {
console.log(name)
}
console.log(name)
}
From the above example, we can see, the declaration is hoisted to the top but the initialization is staying exactly where we type the name variable. This means that we can access the name variable from everywhere in the enclosing block in case of var binding.
Block Level Declarations
- Inside a function (function block)
- Inside a block (wrapped with two curly { } braces)
Let Declarations
We have already seen these block-level scopes in the example above. Now we will use the previous function again but this time using either let or const. So, we are using let :
function getName (condition) {
if (condition) {
let name = 'Nicholas'
console.log(name)
}
else {
// 'name' does not exists here
return null
}
// 'name' does not exists here
}
At the let binding, we can see, name only exists inside the if block. It is not available outside the if block. Because JavaScript engine does not hoist let and const declarations. So the name is not accessible in else and enclosing blocks.
No Redeclaration
var name = "x"
//syntax error
let name = "y"
In this example, name
is declared twice: once with var
and once with let
.Because let
will not redefine an identifier that already exists in the same scope, the let
declaration will throw an error. On the other hand, no error is thrown if a let
declaration creates a new variable with the same name as a variable in its containing scope, as demonstrated in the following code:
Example:
var count = 30;
// Does not throw an error
if (condition) {
let count = 40;
// more code
}
Constant Declarations
You can also define variables in ES6 with the const
declaration syntax. Variables declared using const
are considered constants, meaning their values cannot be changed once set.
Constants vs Let Declarations
Constants, like let
declarations, are block-level declarations. That means constants are destroyed once execution flows out of the block in which they were declared, and declarations are not hoisted, as demonstrated in this example:
if (condition) {
const maxItems = 5;
// more code
}
// maxItems isn't accessible here
Block Binding in Loops
Perhaps one area where developers most want block-level scoping of variables is within for
loops, where the throwaway counter variable is meant to be used only inside the loop. For instance, it's not uncommon to see code like this in JavaScript:
for (var i=0; i < 10; i++) {
process(items[i]);
}
// i is still accessible here
console.log(i);
Functions in Loops
The characteristics of var
have long made creating functions inside of loops problematic because the loop variables are accessible from outside the scope of the loop. Consider the following code:
var funcs = [];
for (var i=0; i < 10; i++) {
funcs.push(function() { console.log(i); });
}
funcs.forEach(function(func) {
func(); // outputs the number "10" ten times
});
Let Declarations in Loops
A let
declaration simplifies loops by effectively mimicking what the IIFE does in the previous example. On each iteration, the loop creates a new variable and initializes it to the value of the variable with the same name from the previous iteration. That means you can omit the IIFE altogether and get the results you expect, like this:
var funcs = [];
for (let i=0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
})
Constant Declarations in Loops
The ECMAScript 6 specification doesn't explicitly disallow const
declarations in loops; however, there are different behaviors based on the type of loop you're using. For a normal for
loop, you can use const
in the initializer, but the loop will throw a warning if you attempt to change the value. For example:
var funcs = [];
// throws an error after one iteration
for (const i=0; i < 10; i++) {
funcs.push(function() {
console.log(i);
});
}
Global Block Bindings
Another way in which let
and const
are different from var
is in their global scope behavior. When var
is used in the global scope, it creates a new global variable, which is a property on the global object (window
in browsers). That means you can accidentally overwrite an existing global using var
, such as:
// in a browser
var RegExp = "Hello!";
console.log(window.RegExp); // "Hello!"
var ncz = "Hi!";
console.log(window.ncz); // "Hi!"
Emerging Best Practices for Block Bindings
While ES6 was released, developers thought let
should behave exactly as var
did. So, the direct replacement makes logical sense. In this case, you would use const
for variables that needed modification protection
However, as more developers migrated to ECMAScript 6, an alternate approach gained popularity: use const
by default and only use let
when you know a variable's value needs to change.
Top comments (0)