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);
Related
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.
This question already has answers here:
What is the purpose of the var keyword and when should I use it (or omit it)?
(19 answers)
Closed 2 years ago.
here i didn't understand what happen when i use var before variable in function it give me different out put and with out using var also i got a different output
here is a code that you can easily figure out whats going on
function value() {
var m = 8; //here i am using var as a datatype
console.log(m)
}
m = 7
console.log(m);
value();
console.log(m);
and also when i removing var or not using any data type from value function then i got different out put here is a code
function value() {
m = 8; //here i am not using var as a datatype
console.log(m)
}
m = 7
console.log(m);
value();
console.log(m);
can any one can tell me whats going on
thanks for your time
Take a look at w3school's JavaScript Scope.
Below is example code of the difference of global variable and local variable.
function value() {
// local variable in value()
var m = 8;
console.log("in value() : " + m)
}
function value2() {
// set global variable as 9
m = 9;
console.log("in value2() : " + m)
}
// global variable
m = 7
console.log("before value() : " + m);
value();
console.log("after value() : " + m);
value2();
console.log("after value2() : " + m);
Below is another case to show the difference of scope:
m = 7;
function v(){
var m = 6;
v2();
function v2(){
console.log("in v2 : " + m);
v3();
}
}
function v3(){
console.log("in v3 : " + m);
}
v();
Function looks for the m in the functional scope, if it does not find it there, it searches in the higher scope which over here is global scope.
In the first example, function creates a variable m in the function scope and any update will be limited to the variable in the function. So, basically in this example there are 2 variables, with one m belongs to the global scope and one m belongs to the function scope. See the interpretation of code below.
var m;
function value() {
var m; // creates a variable m in the function scope
m = 8;
console.log(m)
}
m = 7
console.log(m);
value();
console.log(m);
In the second example, function tries to access a variable m in the function however, does not find there, hence, searches in the higher scope (global scope) and find one. So, basically in this example there is only 1 variable of m belongs to the global scope. See the interpretation of code below.
var m;
function value() {
m = 8; // no function scope, variable, updates global scope
console.log(m)
}
m = 7
console.log(m);
value();
console.log(m);
An identifier (m for example) lookup (getting its value) basically just checks the current scope if the variable is existing there, if not it continues to the parent scope. So inside of your function
function value() {
m = 5;
}
It will do a lookup like this:
function "value" scope -> execution scope -> global scope
So it finally looks it up in global scope. As there is no m variable anywhere, it will declare the variable there, so m becomes part of the global scope. Thats bad cause every script on the page can now access and modify it. If you append a var (or even better a let) it will declare the variable in the current scope (in ms case the value function scope), and when it looks up it directly finds it there. Therefore if you use another m as an identifier in the global scope, they will refer to two different variables.
This question already has an answer here:
Scope of Default function parameters in javascript
(1 answer)
Closed 4 years ago.
ES6 introduced default parameters. I'm trying to understand how inline function default parameters work with this new feature. Specifically how its scoping work.
Take for example the following two functions:
function one(x, f = function (){return x})
{
var x = 5;
console.log([x,f()]);
}
function two(x, f = function (){return x})
{
x = 5;
console.log([x,f()]);
}
one(1);//[5,1]
two(1);//[5,5]
Is it correct to say that, in function one, f keeps it's own closure scope for x in the parameter list, so that when the function redefines x as a new var: var x = 5;, the reference that f has, is not the same as the one inside the function?
If that's the case, is function one equal to function three below:
function three(x,f)
{
var x2 = x;
f = f !== undefined ? f : () => x2;
var x = 5;
console.log([x,f()]); //[5,1]
}
I tried, without luck, to find how this behavior is documented, if someone could point me to the right part of the documentation that would also be great.
You are correct. In both of the top functions x in f refers to the parameter x.
There's some considerations with case 3.
In the third example if f isn't defined and you are returning x2 when calling the function it will be equal to whatever x originally was. When you do x = 5; you aren't changing x2. This is because when you assign x2 = x JavaScript makes a copy not a reference.
Unless the x parameter is passed an array or object x2 will be a copy and not a reference of x.
So if you do three(3) then x2 will always be 3 because you're never changing it.
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.
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