This question already has answers here:
What is the difference between "let" and "var"?
(39 answers)
let keyword in the for loop
(3 answers)
Closed 5 years ago.
As far as I know, 'let' is used for declaring block variable. But it can't declare twice with same name. For example:
'use strict';
let a = 1;
let a = 2; // syntax error
So how to separate each scope in 'for' iteration with the same variable name?
'use strict';
for(let i = 0; i < 3; i++){
setTimeout(function(){
console.log(i);
});
}
// output:
// 0
// 1
// 2
Dose interpreter change the variable name silently? Any info will be appreciated.
I don't think is a duplicated question. Because I really want to ask is the conflict between two theory.
let scopes your variable to the block it's in.
After you've assigned a scope to a, you can't overwrite it. So the second time you use let, it can't handle that.
Correct usage would be:
'use strict';
let a = 1;
a = 2;
In the for loop , i is declaring at the beginning only and in each iteration it change the value of it .
let is used to variable that you will write there value again .
In the first example you define variable with name 'a' so you cant define another with same name , but you can change it value , which happened in the for loop.
'use strict';
let a = 1;
let a = 2; // syntax error
'use strict';
let a = 1;
var a = 2; this will overwrite...
here you specified a value already.. your intention is , it shouldn't change in runtime. so you cant able to change it once declared in block with same type let ..
that why you getting this error..
intention of let is value should not change..
in loop ,
it will be separate on iterate.. so it print value on runtime. so it wont overwrite it...
let allows to create the variable once, but the value of the variable can be changed,
example -
let t =5//
let t = 6 // throws an errow but
t =7
console.log(t) - gives 7
In the loop the variable is not declared again but only its value is incremented.
It is similar to the following:
'use strict';
let i = 0;
i = i + 1;
consider the code being iterated again and again until the condition fails.
for(let i = 0; i < 3; i++){
// code
}
step:
let i = 0
checkout i<3
exec code
i++, equals i = i + 1 // not occur error, while let i = i + 1 will occur exception
loop step-2 until i<3 == false
In your first example the variable a is defined twice in the same scope. This gives you an error because the variable already exists. let doesn't allow this, redeclaring with var does. This is one advantage of using let.
var a = 3;
var a = 2;
console.log(a); // 2
let b = 3;
let b = 2; //syntax error
let c = 3;
var c = 2; //also a syntax error
In a for loop, the i variable has a different scope. Meaning these two i variables here aren't the same.
let i = "my string";
console.log(i); //my string
for(let i = 1; i <= 3; ++i){
console.log(i); //1, 2, 3
}
console.log(i); //my string
The javascript runtime doesn't care if your variable is named the same. It will differentiate the scope and thus the reference to the variable. It's replacing your reference to i whichs value is 4 to the new variable in the for loop.
I have an answer on here on Stackoverflow which describes how the Android compiler doesn't care about your variable names. It's the same here, the runtime uses different "names" (different memory addresses).
Related
Run below code in Node environment. Running it in browser console doesn't allow to redeclare variable of var.
console.log(a);
var a = 5;
According to hoisting, the above code is going to look like this
var a = undefined;
console.log(a); // undefined
a = 5;
a variable is being hoisted to the top of the file. JS Engine allocates memory for this variable before the execution. The question is why below code consols 5 instead of undefined.
var a = 5;
console.log(a);
var a = 6;
I'm looking at this code and imagining that it's going to look like this:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
I'd like to be sure of the answer instead of guessing. The JS Engine is smart enough to see that a variable is already declared and is going to ignore the next var expression and rehoisting in such case? So the output should be looking like:
var a = 5;
console.log(a); // 5
a = 6;
So it's like:
JS Engine sees for the first time declaration (in this case along with initialization) of a variable so it's allocating memory.
JS Engine sees for the second time declaration of a variable but is going to ignore the hoisting because variable of given name is already in the memory.
Am I wrong in something?
Preface: In modern JavaScript, var should never be used. Use let or const.
The JavaScript engine handles var in two steps:
Upon entering the global scope or a function scope, it processes every var in the entire scope, defining variables for the them initialized wit the value undefined. If a variable is declared more than once with var, it's exactly as though it were declared once.
Then it starts the step-by-step execution of the code. In that step-by-step execution, any initializer on a var statement (the = 5 in var a = 5) is considered an assignment. So var a = 5 is treated exactly like a = 5 at this point.
So in your example:
var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
It's as though you had written this:
var a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
var a = 6;
or this:
var a;
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
or this:
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
var a;
or even this (but please don't! :-) ):
var a = 5;
var a = undefined;
console.log(a); // undefined
var a = 6;
var a;
That is, first all the variables declared with var are created (and only once), then the code runs as though any initializers on them were assignments.
This is not how let, const, and class declarations are handled (collectively: lexically-scoped declarations). First: Multiple declarations in the same scope are an error (including if one of them is with var and the other is with one of the lexically-scoped ones). Second: They're hoisted (in Step 1 above), but the hoisted binding¹ is uninitialized until the declaration is executed in the step-by-step code, at which point it's initialized (either with the value from the initializer, or with undefined if it's just e.g. let a;). The time between entry to the scope and the point the binding is initialized is called the Temporal Dead Zone. var doesn't have it because var variables are initialized when they're created (with the value undefined), but let, const, and class declarations do.
¹ The term binding is the general term for variable-like things. In the code:
function example(p) {
var v;
let l;
const c = 42;
function f() {
}
class C {}
}
the bindings created upon entering the example function's scope are p (a parameter), v (a var variable), l (a let variable), c (a const constant), f (the binding created by a function declaration), and C (the binding created by a class declaration). (Note: function and class expressions are handled slightly differently.)
This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
Closed 5 years ago.
function first(){
var items = document.getElementsByTagName("li");
for(var x = 0; x < items.length; x++){
items[x].onclick = function() {
console.log(x);
}
}
}
function second(){
var items = document.getElementsByTagName("li");
for(var x = 0; x < items.length; x++){
(function(val) {
items[val].onclick = function() {
console.log(val);
}
})(x);
}
}
function third(){
var items = document.getElementsByTagName("li");
for(let x = 0; x < items.length; x++){
items[x].onclick = function() {
console.log(x);
}
}
}
There are 4 elements in the list. Outputs of the 3 functions:
first: 4 4 4 4
second: 0 1 2 3
third: 0 1 2 3
I am not able to understand the output from the third function. In the second function, each call to the IIFE creates a new function object and hence, a new val variable. But in the third function, there is a single copy of the variable x, then how is the output: 0 1 2 3
Please correct me if I am wrong.
In the documentation for let from MDN they have an example covering this exact case in the Cleaner Code in inner functions section:
for (let i = 1; i <= 5; i++) {
let item = document.createElement('li');
item.appendChild(document.createTextNode('Item ' + i));
item.onclick = function(ev) {
console.log('Item ' + i + ' is clicked.');
};
list.appendChild(item);
}
The example above works as intended because the five instances of the (anonymous) inner function refer to five different instances of the variable i. Note that it does not work as intended if you replace let with var, since all of the inner functions would then return the same final value of i: 6. Also, we can keep the scope around the loop cleaner by moving the code that creates the new elements into the scope of each loop.
The same thing applies to your case, because you use let each anonymous function refers to a different instance of x. There is a different instance on each iteration of the loop. This happens because let has a block-level scope instead of the global function scope that var has.
From the docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
let allows you to declare variables that are limited in scope to the
block, statement, or expression on which it is used. This is unlike
the var keyword, which defines a variable globally, or locally to an
entire function regardless of block scope.
When it is var it gets hoisted like all variables.
When it is let the scope is the block it is defined in.
This is one of the trickiest examples of let keyword.
The fact that Let binds variables to the block(& in this case for-loop) means that it binds the variable to every iteration of the loop. So, when loop is finished, you have 4 items (items from item[0] to item[3]) listening to click event.
In fact the for-loop in the third function produces the following:
items[0].onclick = function() {
console.log(0);
}
items[1].onclick = function() {
console.log(1);
}
items[2].onclick = function() {
console.log(2);
}
items[3].onclick = function() {
console.log(3);
}
Make sure to read more about Let here in MDN Some other exciting cases could be found there.
I have been using python for a while now, and have just started learning javascript. In javascript you can, as I understand it, declare a variable without assigning a value to it (var cheese compared to var cheese = 4) in what situation would you want to declare a variable but not assign a value to it straight away?
Consider this snippet.
if (someCondition) {
var x = 5;
} else if (someOtherCondition) {
var x = 4;
}
if (x) {
doFunc();
}
Since x needs to exist for the doFunc to run, you simply add an undefined declaration above. var x; so that the if (x) doesn't return an error.
You do this when you want the value of the variable to be undefined.
var cheese;
console.log(cheese); // undefined
It's simpler than
var cheese = undefined;
The undefined value doesn't seem much useful, but this will allow to assign some other value later.
var cheese; can be perfectly useful (even if you never assign anything to it). Of course it's a shorter way to type var cheese = undefined;, but that's not the only reason…
Using var declares a local variable, and this has a nice property: it hides variables from parent scopes.
There's another part to your question:
If we're going to assign a value to var cheese anyway: why not assign immediately?.
Answer: it may be fine for your algorithm to return cheese without ever assigning anything to it — i.e. "undefined is valid".
Here's an example which illustrates how var hides variables from parent scopes:
var a = 3;
console.log(a); // prints 3; "a" is defined in this scope
function findEvenNumber(numbers) {
var a; // we declare this local variable, to ensure that we do _not_ refer to the variable that exists in the parent scope
numbers.forEach(function(number) {
if (number % 2 === 0) {
a = number;
}
});
return a; // if no even number was found, it returns undefined (because we never assigned anything to a)
}
findEvenNumber([1, 2]); // returns 2;
console.log(a); // prints 3; the "a" in this scope did not get overwritten by the function
Speculation: maybe the var cheese; syntax exists in ECMA to enable programmers to declare all variables at the beginning of their function. Such a convention was enforced by the C89 compiler, and some people grew fond of it.
While learning about javascript closures, I came across this answer https://stackoverflow.com/a/111111/3886155 on stackoverflow.
It's a very good explanation of closures.
But I have some confusion in Example 4 and Example 5.
I just copy entire snippet here:
Example 4:
var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
// Local variable that ends up within closure
var num = 666;
// Store some references to functions as global variables
gLogNumber = function() { console.log(num); }
gIncreaseNumber = function() { num++; }
gSetNumber = function(x) { num = x; }
}
setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 667
gSetNumber(5);
gLogNumber(); // 5
var oldLog = gLogNumber;
setupSomeGlobals();
gLogNumber(); // 666
oldLog() // 5
After reading some examples I can say that whenever function inside the function executes it can always remember the variables declared inside outer function.
I agree that if these closure variable updated anyway it still refers to the new variable value.
My problem in this example is specially related to var oldLog=gLogNumber;
How it can return old number after call to the setupSomeGlobals();?
because now the var num has reset.So why it is not using this new num value 666?
Now Example 5:
function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
var item = 'item' + i;
result.push( function() {console.log(item + ' ' + list[i])} );
}
return result;
}
function testList() {
var fnlist = buildList([1,2,3]);
// Using j only to help prevent confusion -- could use i.
for (var j = 0; j < fnlist.length; j++) {
fnlist[j]();
}
}
Here they have pushed functions into array and executed them after loop finishes.But now reference to the closure variables is the latest one after loop finishes.Why not old one?
In both examples you are just assigning function definition to variable or array index.But first one points to old and second one points to latest.Why?
How it can return old number after call to the setupSomeGlobals()
num is not globally scoped, so the value num is in the context of which reference of gLogNumber is invoked.
After invocation of setupSomeGlobals method again, reference to gLogNumber got changed. try this
console.log(Object.is( gLogNumber, oldLog )); //true
setupSomeGlobals();
console.log(Object.is( gLogNumber, oldLog )); //false
So, oldLog retained old reference and hence old value of num, but gLogNumber got new num.
But now reference to the closure variables is the latest one after
loop finishes.Why not old one?
For this problem, have a look at
JavaScript closure inside loops – simple practical example.
This uses var
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 10
This uses let
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
};
}
a[6](); // 6
I don't understand why the result is different. Can somebody guide me?
The resulting array consists of functions, each function body looks like this:
console.log(i);
The value of i depends on whether we used var or let to declare the variable.
var (ECMAScript 5 and 6)
Here i is a global variable whose value is 10 after exiting the loop. This is the value that is logged.
let (ECMAScript 6)
Here i is a local variable whose scope is restricted to the for statement. Moreover, this variable gets a fresh binding on each iteration. This is best explained by your code transpiled to ECMAScript 5:
"use strict";
var a = [];
var _loop = function(i) {
a[i] = function() {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
a[6](); // 6
So, on seventh iteration for example, the value of i will be 6 (counting from zero). The function created inside the iteration will refer to this value.
I think it would be much better to not define functions in a loop, you could easily accomplish this with one function definition that returns a closure:
function logNumber(num) {
return function() {
console.log(num);
}
}
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = logNumber(i);
}
a[6]();
Regarding the difference between the two examples, one is using let for block scoping. A better example that shows the difference would be:
ECMA5:
for (var i = 0; i < 10; i++) { }
console.log(i); // 10
ECMA6:
for (let i = 0; i < 10; i++) { }
console.log(i); // i is not defined
Edit: as I stated in my comment to your question, this is more likely a side-effect of the transpiler you are using. Firefox supports block scoping and both versions of your loop produce 10 as output (which they should).
This is correct behavior according to the spec. The behavior with var and let is defined to be different.
See the spec, at https://people.mozilla.org/~jorendorff/es6-draft.html#sec-forbodyevaluation. According to this, the relevant concepts, which make the function declared inside the loop close over the current value of the block-scoped loop index, are things called "per-iteration bindings" and "per-iteration environment".
Babel handles it correctly, producing the following code:
var a = [];
var _loop = function (i) {
a[i] = function () {
console.log(i);
};
};
for (var i = 0; i < 10; i++) {
_loop(i);
}
This implements the semantics of for (let by isolating the contents of the for loop into a separate function parameterized by the index. By virtue of doing that, the function no longer closes over the for loop index, and i is treated separately in each function created. Thus the answer is 6.
Traceur does not produce the correct result. It yields 10.
So the famous question that has been asked 100 times on SO, about why my function declared in a loop and closing over the index index is using the "wrong" value of the loop index, shall be asked no more?
The issue is a bit more nuanced that merely proclaiming that "of course, let is block-scoped". We know that. We get how it works in an if block, for example. But what's going on here is a bit of an twist on block scoping in the context of a for, hitherto unknown to many people including me. It's a variable actually declared outside the "block" (if you think of the block as the body of the for statement) but has a separate existence inside each iteration of the loop.
For more, see https://github.com/babel/babel/issues/1078.
Why is result different in ES6 and ES5?
Because let and var are different. let is block-scoped while var is function-scoped.
In your first example there is only a single variable i. Every function you create has a reference to the same variable i. At the moment you call a[6](), i has the value 10, because that was the termination condition for the loop.
In the second example, every iteration of the loop has it's own variable i. It works exactly like in other languages with block scope.