Block Bindings
Var Declarations and Hoisting:
Variable declarations using var are treated as if they are at the top of the function (or global scope, if declared outside of a function) regardless of where the actual declaration occurs; this is called hoisting. Consider the examples below:function getValue(condition) {
if (condition) {
var value = "blue";
// other code
return value;
} else {
// value exists here with a value of undefined
return null;
}
// value exists here with a value of undefined
}
Behind the scenes, the JavaScript engine changes the getValue function to look like this:
function getValue(condition) {
var value;
if (condition) {
value = "blue";
// other code
return value;
} else {
return null;
}
}
The declaration of value is hoisted to the top. The variable value is actually still accessible from within the else clause. If accessed from within the else clause, the variable value will be undefined because it hasn’t been initialized, its initialized only in If clause.
Block-Level Declarations:
1.Inside of a function
2.Inside of a block { \\code block }
Let Declarations
The let declaration syntax is the same as the syntax for var. You can basically replace var with let to declare a variable, but the limit is only the current code block. Here’s an example:
function getValue(condition) {
if (condition) {
let value = "blue";
// other code
return value;
} else {
// value doesn't exist here
return null;
}
// value doesn't exist here
}
No Redeclaration:
If an identifier has already been defined in a scope, then using the identifier in a let declaration inside that scope causes an error to be thrown. For example:
var count = 30;
// Syntax error
let count = 40;
This next let declaration exaple doesn’t throw an error because it creates a new variable within the if statement. Inside the if block, this new variable shadows the global count but prevents access to it until execution leaves the block.
var count = 30;
// Does not throw an error
if (condition) {
let count = 40;
// more code
}
Constant Declarations:
You can also define variables with the const declaration syntax. Variables declared using const are considered constants, meaning their values cannot be changed once set. For this reason, every const variable must be initialized when declaration, as shown in this example:
// Valid constant
const maxItems = 30;
// Syntax error: missing initialization
const name;
Constants vs Let Declarations:
Constants are like let declarations which is block-level declarations. That means constants are no longer accessible once execution flows out of the block, const doesn't hoisted, as demonstrated in this example:
if (condition) {
const maxItems = 5;
// more code
}
// maxItems isn't accessible here
A const declaration throws an error when made with an identifier which is already-defined variable in the same scope. It doesn’t matter variable was declared using var or let. For example, consider this code:
var message = "Hello!";
let age = 25;
// Each of these would throw an error.
const message = "Goodbye!";
const age = 30;
Attempting to assign a const to a previously defined constant will throw an error, in both strict and non-strict modes:
const maxItems = 5;
maxItems = 6; // throws error
It wasn't throws error in case of let. As example below:
let maxItems = 5;
let = 6; // throws error
Declaring Objects with Const:
A const declarations for objects do not prevent modification of the objects. For example:
const person = {
name: "Nicholas"
};
// works
person.name = "Greg";
// throws an error
person = {
name: "Greg"
};
**Just remember: const prevents modification of the binding, not modification of the bound value.
The Temporal Dead Zone:
A variable declared with either let or const cannot be accessed until after the declaration. Follow this example:
if (condition) {
console.log(typeof value); // ReferenceError!
let value = "blue";
}
Block Binding in Loops:
This is onne 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 for this we have to use let not var, cause var is being hoisted. Follow the two exaples below:
for (var i = 0; i < 10; i++) {
process(items[i]);
}
// i is still accessible here
console.log(i); // 10
The variable i is still accessible after the loop is completed because the var declaration gets hoisted. Using let instead, as in the following code to ignore this problem:
for (let i = 0; i < 10; i++) {
process(items[i]);
}
// i is not accessible here - throws an error
console.log(i);
In this example, the variable i only exists within the for loop.
Functions in Loops:
The characteristics of var have 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
});
It outputs the number 10 ten times in a row. The variable i has a value of 10 once the loop completes, and so when console.log(i) is called, that value prints each time.
To fix this problem, developers use immediately-invoked function expressions (IIFEs) inside of loops to force a new copy of the variable they want to iterate over to be created, as in this example:
var funcs = [];
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value);
}
}(i)));
}
funcs.forEach(function(func) {
func(); // outputs 0, then 1, then 2, up to 9
});
**Fortunately, block-level binding with let and const in ECMAScript 6 can simplify this loop for you.
Let Declarations in Loops:
On each iteration, the loop creates a new variable and initializes it to the new value with the previous same name. Consider the example:
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
})
For more clearance about the fact follow this example also:
var funcs = [],
object = {
a: true,
b: true,
c: true
};
for (let key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // outputs "a", then "b", then "c"
});
In this example, the for-in loop shows the same behavior as the for loop. Each time through the loop, a new key binding is created, and so each function has its own copy of the key variable. The result is that each function outputs a different value. If var were used to declare key, all functions would output "c".
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 when 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);
});
}
In this code, the i variable is declared as a constant. The first iteration of the loop, where i is 0, executes successfully. An error is thrown when i++ executes because it’s attempting to modify a constant.
When used in a for-in or for-of loop, on the other hand, a const variable behaves the same as a let variable. So the following should not cause an error:
var funcs = [],
object = {
a: true,
b: true,
c: true
};
// doesn't cause an error
for (const key in object) {
funcs.push(function() {
console.log(key);
});
}
funcs.forEach(function(func) {
func(); // outputs "a", then "b", then "c"
});
The for-in and for-of loops work with const because the loop initializer creates a new binding on each iteration through the loop rather than attempting to modify the value of an existing binding (here key is a, b and c).
Global Block Bindings:
Another way in which let and const are different from var. When var is used in the global scope, which is a property on the global object (window in browsers). That means you can 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!"
If you instead use let or const in the global scope, a new binding is created in the global scope but no property is added to the global object. That also means you cannot overwrite a global variable using let or const, you can only shadow it. Here’s an example:
// in a browser
let RegExp = "Hello!";
console.log(RegExp); // "Hello!"
console.log(window.RegExp === RegExp); // false
const ncz = "Hi!";
console.log(ncz); // "Hi!"
console.log("ncz" in window); // false
This capability makes let and const a lot safer to use in the global scope when you don’t want to create properties on the global object.
Summary of Block Bindings:
The let and const block bindings introduce lexical scoping to JavaScript. These declarations are not hoisted and only exist within the block in which they are declared. This offers behavior that is more like other languages and less likely to cause unintentional errors, as variables can now be declared exactly where they are needed. As a side effect, you cannot access variables before they are declared, even with safe operators such as typeof. Attempting to access a block binding before its declaration results in an error due to the binding’s presence in the temporal dead zone (TDZ).
In many cases, let and const behave in a manner similar to var; however, this is not true for loops. For both let and const, for-in and for-of loops create a new binding with each iteration through the loop. That means functions created inside the loop body can access the loop bindings values as they are during the current iteration, rather than as they were after the loop’s final iteration (the behavior with var). The same is true for let declarations in for loops, while attempting to use const declarations in a for loop may result in an error.
The current best practice for block bindings is to use const by default and only use let when you know a variable’s value needs to change. This ensures a basic level of immutability in code that can help prevent certain types of errors.
No comments:
Post a Comment