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