ES6 javascript block scope variable hoisting into for header - javascript

Are all of the block scope variables in a for loop block hoisted above the loop header itself?
var x = 4;
for(let i = 3; i < x; i++) {
let x = 2;
...
}
Should this produce a dead-zone error on x every time i is compared to x in the loop header? I understand i is pushed down in the block scope, but why not x?

The relevant section of the language specification is 13.7.4
If the for statement contains a let or const declaration then a scope is created.
Each iteration creates a scope, if the for statement contains a let declaration.
If the for body is a block then a scope is created.
Here are some examples and the scopes that are created:
//no scope
for(i = 0; i<3; i++) console.log(i);
//no scope
for(var i = 0; i<3; i++) console.log(i);
//for scope and iteration scope
for(let i = 0; i<3; i++) console.log(i);
// for scope, iteration scope and block scope
for(let i = 0; i<3; i++) {
console.log(i);
}
Why do we need iteration scopes? For closures:
for(let i = 0; i<3; i++) {
setTimeout(() => console.log(i), 10);
}
Output without iteration scope: 3,3,3. With iteration scope: 0,1,2

based on that, i think let x are only available in that for scope.

Are all of the block scope variables in a for loop block hoisted above the loop header itself?
No, they are hoisted in the block scope
Should this produce a dead-zone error on x every time i is compared to x in the loop header? I understand i is pushed down in the block scope, but why not x?
No, because x in the loop header is referencing x defined as 4 var x = 4
You cannot access a let variable before it has been declared as that time between scoping and declaration is the TDZ.
ex.
console.log(x) // Reference Error, x is in TDZ
let x = 4
console.log(x) // --> 4

I misunderstood, and thought that there would be only one block scope. I balked at taking the two arbitrary variables in the expression i < x and having to decide which block scope to look them up, which is impossible to determine.
The correct approach is to build two nested block scopes, one for the loop header and one for the loop body. That means let i is seen in both the loop header and the loop body scopes, and the let x in the loop is only seen in the loop body scope. Easy peasy, lemon squeezy.

Related

Why does i = 3 outside the for loop?

The condition in the loop specifies i<3. Shouldn't the loop stop at i = 2? if so, shouldn't outside loop be 2 not 3?
Thanks.
for (var i = 0; i < 3; i++){
console.log(i, " loop")
if(i%2===0){
console.log (i,'even numbers in loop ');
}
}
console.log(i, " outside loop")
That's the correct behaviour of loops.
The loop starts with i = 0, then i = 1, then i = 2, then i = 3. oops i is no more less than 3, break the loop. that's what's going on, so when you log the value of i you get 3 and not 2.
Loop: it breaks as soon as i < 3 is not verified, which means in the last iteration. More informations about iterations.
Scope: because i is declared using var, which attaches the variable to the global scope. Using var is highly discouraged, use let instead, which declares variables in block scope. More informations about var and let.

Javascript declaring a variable inside the initialization step

I am watching a video about C programming, and the instructor has an example for loop that is written as such:
for(int i=0, n=strlen(c); i<n; i++)
In Javascript can we declare n in the same way as well? I'm trying to not set "i
Edit:
Thank you for the replys. Sounds like I can't do the code from above, but instead I would have to split it up into 2 separate line items like so:
const n = strlen(c);
for(int i=0; i<n; i++)
In Javascript can we declare n in the same way as well?
Yes. You can use either var or let there, and it makes a big difference: Inside the loop's block, if you use var, all loop iterations share a single variable. If you use let, each loop iteration gets its own variable. This only matters if you create closures within the loop.
Example of the difference:
for (var varvar = 0; varvar < 3; ++varvar) {
setTimeout(() => {
console.log(`varvar = ${varvar}`);
}, 10);
}
for (let letvar = 0; letvar < 3; ++letvar) {
setTimeout(() => {
console.log(`letvar = ${letvar}`);
}, 20);
}
(And if you use var, the variable exists throughout the function [or global scope, if you're doing this at global scope]. With let, it's confined to the for loop.)
for(let i = 0; i < c.length; i ++)

JavaScript for loop with let

My JavaScript book, "JavaScript The Definitive Guide, 6th Ed.", page 270 includes this text and code:
"... in a for loop, the initializer expression is evaluated
outside the scope of the new variable"
let x = 1;
for (let x = x + 1; x < 5; x++) {
console.log(x); // prints 2, 3, 4
}
When I run the above code (in the latest version of Chrome and FF) however, I get console errors:
ReferenceError: x is not defined
can't access lexical declaration `x' before initialization
Is the code in the book incorrect? (There's nothing on the book's errata site re: this.)
The problem is not really that x gets declared twice. It is just that you are trying to access the inner x before it got initialized:
let x = x /* doesn't exist yet*/;
Wether there is another x in the outer scope (initializers in a for loop are inside their own scope) doesn't matter, the x will refer to the variable in the current scope, as it already got declared (due to hoisting), but wasn't initialized yet:
let x = 0; // irrelevant
{ // x gets declared as part of this scope
x; // thats an error too as x is not initialized yet
let x = 1; // initialization
x; // now it can be accessed
}
The part between the beginning of a scope and a let declaration is called the "temporal dead zone" ...
"... in a for loop, the initializer expression is evaluated outside the scope of the new variable"
No, otherwise you couldn't refer to other variables in the initializer:
for(let a = 1, b = a; ; )
As always, the definite answer can be found in the spec:
13.7.4.7 Runtime Semantics: LabelledEvaluation
IterationStatement : for ( LexicalDeclaration Expression ; Expression ) Statement
Let oldEnv be the running execution context's LexicalEnvironment.
Let loopEnv be NewDeclarativeEnvironment(oldEnv).
[...]
Let boundNames be the BoundNames of LexicalDeclaration.
For each element dn of boundNames [..]
Perform ! loopEnvRec.CreateImmutableBinding(dn, true).
Set the running execution context's LexicalEnvironment to loopEnv.
Let forDcl be the result of evaluating LexicalDeclaration.
[...]
As you can see, the running execution context is loopEnv while the LexicalDeclaration (the initializers) gets evaluated, not oldEnv.
TLDR: Not only the example is wrong, but also the paragraph.
The only issue is that x is being redeclared shadowed (as mentioned by Jonas above), hence it throws an error.
Just remove the second let and everything will work as expected.
let x = 1;
for (x = x + 1; x < 5; x++) {
//^---- note the missing "let" here.
console.log(x); // prints 2, 3, 4
}
If you copied that from a book, then that's a book issue.
https://jsfiddle.net/hto9udmj/
Further infos about variable declarations can be found here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
Further infos about variables shadowing:
An example of variable shadowing in javascript
Is the code in the book incorrect? (There's nothing on the book's errata site re: this.)
I believe the books was correct; when let was introduced for the first time years ago in Firefox.
Specifically, it didn't have the temporal dead zone, and it behaves internally more like var, just block scoped.
In Firefox 44, there was a breaking change that makes let and const following the standards:
https://blog.mozilla.org/addons/2015/10/14/breaking-changes-let-const-firefox-nightly-44/
Including the introduction of the temporal dead zone.
So, yes, the book now is incorrect; since you're trying to do something like:
let x = 0;
{
let y = x; // `let` is block-scope,
// so this `x` is actually the `x`
// defined below, not the one outside
// the scope, hence the `ReferenceError`.
let x = 1;
}
You are initializing x twice and thus getting error. Rename one x to i
let x = 1;
for (let i = x + 1; i < 5; i++) {
console.log(i); // prints 2, 3, 4
}
The problem is that you redeclare x within the for loop and since let only exists within the given context, after the loop is done x doesn't exist anymore.
Either declare x once outside of the for loop, or use var. var adds the variable to a global scope, so it'll exist after the for loop is done.
let x = 1;
for (x = x + 1; x < 5; x++) {}
console.log(x);
With var:
for (var x = 2; x < 5; x++) {}
console.log(x);

weird syntax for "for" loop

Today I came across the following JS:
for (var b = k, d = function (c) {
// function body
}, a = b.p, e = z; 0 < c;) {
} // for loop closing cause
Can some please explain what's happening here? I'm confused about the function, normal "for" loop would do:
for(x=0; x<something; x++){
}
This loops appears to follow a different structure:
for(x=0; d = function(c), a = somevar, e = somevar, 0 < c)
Why are there 5 elements in the for loop? Thanks!
for (var b = k, d = function (c) {}, a = b.p, e = z; 0 < c;) {
} // for loop closing cause
for(x=0; x<something; x++)
If you map the first for loop with the signature for loop...
The code in bold is the declaration part for different variables that are going to be used inside the for loop.. .. Any number of variables can be declared here...
The second is the check condition...
And the third part is empty
There are not five parts, only three:
The first one contains four variable declarations (b, d, a and e).
The second one contains a comparison (0 < c).
The third one is empty.
Each part is separated by semicolons (;) and the variable declarations are separated by commas ,.
The first part is allowed to contain either an expression or a variable declaration list. For more information, have a look at the specification or the MDN JavaScript documentation:
initialization
An expression (including assignment expressions) or variable declaration. Typically used to initialize a counter variable. This expression may optionally declare new variables with the var keyword. These variables are not local to the loop, i.e. they are in the same scope the for loop is in. The result of this expression is discarded.
Only because for(x=0; x<something; x++) is the most typical form, does not mean others don't exist. Each part can contain an arbitrary expression. For example, a useful way to iterate over all children of a DOM element is the following:
for(var node = element.firstChild; node; node = node.nextSibling) {
}
First, node is initialised with the first child of the element. The second part just tests whether node is not null (you could also write node !== null explicitly) and the third part (executed after the iteration) assigns the next child to node.
Ill explain, but basically this is not good code.
Its not good code because you need to go online just to figure it out.
for (var b = k, d = function (c) {
// function body
}, a = b.p, e = z; 0 < c;) {
} // for loop closing cause
is the equivalent of
var b = k,
d = function (c) {
// function body
},
a = b.p,
e = z;
for (; 0 < c; ){
}
for loop parents has three parts
1. Initialization
2. Condition
3. Ending statement
That must all be separated by a ;
except these are all optional in a for loop
for (var init = 0; init < 10; init += 1) {}
same as:
var init = 0;
for (;;) {
if (!(init < 10)) {
break;
}
init += 10;
}
The control variables in a loop can also be a valid expressions as you have in your example.
The first loop you posted is slightly different from the last. The first declares and assigns several variables (one of which is a function), then provides a condition (0<c), then does nothing every iteration. The last seems invalid.
The problem with the first one seems to be only that it does not intialize a c, so unless c is a variable from outside of the scope of the loop that is somehow being changed inside its body, the loop will either not run at all (if c>=0) or it will run forever (if c is indeed less than 0).
for (var b = k, d = function (c) {
// function body
}, a = b.p, e = z; 0 < c;) {
} // for loop closing cause
as long as d is not 0 the for loop will run. d is 0 when 0 < c

How to create closure in loop and store it in variable for later execution

See code below. I've tried to strip it to its bare bones.
I have a _queue array. I want to iterate 10 times. On each iteration, I want to create a function that has a properly scoped reference for j (i.e. j=0 on the first iteration, j=1 on the second iteration, etc.)
I want to store that function in variable f, and then add f to the _queue array so I can call it later.
The problem of course is that on each iteration of the first loop, instead of storing the closure in f, it immediately executes the closure.
My question is this: How do I store the function with its proper j variable so that I can add it to the _queue array?
_queue = [];
for (j = 0; j < 10; j++) {
var f =
(function (index) {
alert(index);
})(j); //code is executed here instead of stored in the f variable
_queue.push(f); //Add f
}
for (k = 0; k < _queue.length; k++){
_queue[k].call();
}
Using an immediate function (or in general using a function) to introduce a new scope is correct. But you have to return a function from the immediate function:
var f = (function (index) {
return function() {
alert(index);
};
}(j));

Categories

Resources