Why is result different (using var vs. let)? - javascript

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.

Related

Inconsistent scope rules of variables in for, for-in and for-of loops

So I noticed that I have to use let inside a for loop, and cannot use const. However, I found that I can use const inside the for-in and for-of constructs (code below). Intuitively I can rationalize that this is because the for loop is implemented differently/is more primitive, whereas the other constructs desugar into for loops where the iterating variable is assigned at the top of the for loop.
// Doesn't work
for (const i = 0; i < 3; i++) {
console.log(i);
}
// Works
for (let i = 0; i < 3; i++) {
console.log(i);
}
// Works
const object2 = ['a', 'b', 'c'];
for (const v of object2) {
console.log(v);
}
// Works
const object3 = {
a: 'a',
b: 'b',
c: 'c',
};
for (const v in object3) {
console.log(v);
}
The only thing I could find on Mozilla MDN about this was on the for loop page:
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.
Which also seems wrong, because if we use a let for i then i is no longer in scope after the for loop (which is consistent with other languages)
for (let i = 0; i < 3; i++) {
console.log(i);
}
// Doesn't work as expected
console.log(i);
My question is whether this behaviour is expected and defined in the spec somewhere? MDN doesn't say much about this.
Yes. This is indeed expected behavior.
const defines a variable which, as the name suggested, stays constant. That means the value of a const cannot change.
Now what you do in your for loop is incrementing "i", which was defined as a constant.
for (const i = 0; i < 3; i++ /* <- this doesn't work */ ) {
console.log(i);
}
with for .. in or for .. of however, you just bind the variable.
In other words: With for .. in/off, the variable gets assigned once before execution of the loop and not on every iteration. Therefore const can indeed be used.
As for the reference:
ForDeclaration : LetOrConst ForBinding
http://www.ecma-international.org/ecma-262/6.0/index.html#sec-for-in-and-for-of-statements-static-semantics-boundnames
So I noticed that I have to use let inside a for loop, and cannot use const.
No. You can use a const declaration in a for loop just fine. The problem just is that const declares a constant binding, so an increment i++ doesn't work on const i (it should throw an exception, make sure you're in strict mode).
An example of how to use const:
for (const o = {index: 0, value: null}; o.index < arr.length; o.index++) {
o.value = arr[o.index];
doSomething(o);
}
Or one where it makes more sense:
for (const iterator = makeIterator(); !iterator.isDone(); iterator.next())
doSomething(iterator.getCurrent());
}
Intuitively I can rationalize that this is because the for loop is implemented differently/is more primitive, whereas the other constructs desugar into for loops where the iterating variable is assigned at the top of the for loop.
Yes. In a for loop, you need to take care of updating the iteration variables yourself.
for ([var] init; condition; update) {
body
}
becomes
[var] init;
while (condition) {
body;
update;
}
for (const init; condition; update) {
body
}
becomes
{
const init;
while (condition) {
body;
update;
}
}
for (let init; condition; update) {
body
}
becomes something more complicated
In for … in and for … of loops, you just declare an assignment target expression for the produced value.
for ([var]/let/const target of iterable) {
body
}
becomes
{
const _iterator = iterable[Symbol.iterator]();
let _result;
while (!(_result = _iterator.next()).done) {
[var]/let/const target = _result.value;
body;
}
}
for (… in enumerable) is just the same as for (… of Reflect.enumerate(enumerable)).
The only thing I could find on Mozilla MDN about this was on the for loop page, which also seems wrong.
Yes, looks like that section hasn't yet been updated for ES6.
As per spec
ForDeclaration : LetOrConst ForBinding
let and const is allowed in for-in and for-of statements.
Also, as per run-time semantics mentioned in spec
For each element name of the BoundNames of ForBinding do
This expression for (const v in object3) { is executed for each iteration and give a new binding.
However, with simple for-loop - for (const i = 0; i < 3; i++) {, const i is only executed once and hence it doesn't allow you to re-assign a value to it.
Your first question has been answered by #NullDev, so I'm going to the second:
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.
"These variables are not local to the loop" means the counter created by var keyword. If you use let then the scope of the counter is only in that for loop. This is another expected behavior since var has the wideless scope. And yes, the documentation is a little bit ambiguous.

How to understand 'let' in 'for' statement? [duplicate]

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).

Closure (let keyword) - Javascript [duplicate]

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.

Behaviors of Javascript closures and global functions

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.

Javascript extension built-in classes

I found an example of the expansion of the built-in javascript
var n = 3;
Number.prototype.times = function(f, context) {
var n = Number(this);
for (var i = 0; i < n; i++) {
f.call(context, i);
}
};
n.times(function(n) {
console.log(n);
});
I understand that I call the function f (console.log) through the object context.
I understand that we pass this function a serial number from 0 to n.
The method time accepts only one argument - function console.log
Question: Where in the code appeared object context?
Thank you all
A function allows any number of parameters to be supplied. If there's too many, they're not used and if there's too few, the ones that are left out are undefined. n.times(myFunction) is the same as n.times(myFunction, undefined), but just shorter to write.
The context is actually not correctly named, scope would be better. When omitted, the global scope is used. The following is the same:
n.times(myFunction.bind(scope));
n.times(myFunction, scope);
More about scopes and binding:http://www.reactive.io/tips/2009/04/28/binding-scope-in-javascript/
Now all is clear, it was a difficult example.
To understand so much clearer:
var n = 3;
Number.prototype.times = function(f) {
var n = Number(this);
for (var i = 0; i < n; i++) f(i);
};

Categories