In JS, is there any difference between the following two types of loops?
A for
loop without an increment such as:
for (i = 0; i < 1000;) { // i will be changed inside the block
vs
A for
loop with a variable defined, but one that is not being incremented? Like this:
for (i = 0; i < 1000; i) { // i changed inside block
I saw this strange construct in several Stack Overflow questions.
i
meant to note the i
variable changes, not that i
myself changed something or that i
have been changed in some way.
Under normal circumstances: no, there is no difference.
The for statement creates a loop that consists of three optional expressions, enclosed in parentheses and separated by semicolons, followed by a statement (usually a block statement) to be executed in the loop.
The three expressions (initialization, condition and final-expression) are all optional (unlike the semicolons themselves), so if you omit the final-expression (which is most commonly used to increment the i
/index/counter) that part simply will not be used.
If you use an isolated i
as the final-expression, then this will be evaluated after every iteration of the for-loop. But an isolated variable on its own usually doesn't have any side effects. The value wont change and the value that is returned by the i
expression (the value of i
) is ignored in case of the final-expression.
console.log('For loop without final-expression:');
for (let i = 0; i < 10;) {
console.log(i++);
}
console.log('For loop with final-expression:');
for (let i = 0; i < 10; i) {
console.log(i++);
}
But that is all under normal circumstances. There are cases where it does make a difference, but you really should never encounter this in production-ready code.
const target = {
i: null,
};
const proxy = new Proxy(target, {
get(target, prop) {
if (prop === 'i') {
return target._i++;
}
return Reflect.get(...arguments)
},
set(target, prop, value) {
if (prop === 'i') {
target._i = value;
} else {
return Reflect.set(...arguments);
}
}
});
with(proxy) {
console.log('For loop without final-expression:');
for (i = 0; i < 10;) {
console.log("loop");
}
console.log('For loop with final-expression:');
for (i = 0; i < 10; i) {
console.log("loop");
}
}
In the above example I used a Proxy
to be able to intercept all access to the properties of an object and I used a with
statement so that every variable is treated as a property of this proxied object. As a result, when i
is read, the getter
of the proxy is called, and I increment the value of i
every time this happens.
In for (i = 0; i < 10; i)
, i
is set once at i = 0
and read twice in i < 10
and the final-expression i
. Because it is read twice, the i
will be incremented twice, so the loop will only iterate 5 times. In the other for (i = 0; i < 10;)
loop i
is only read once, so the loop will iterate 10 times.
There are likely more ways to achieve a similar effect, but again you should really never encounter this in production ready code, so it shouldn't be taken into account when deciding if you want to keep the i
in the final-expression or not.
Personally I would say that if you don't use an optional expression, then you should leave it out. Especially because when glancing over the code, i
can be misread as i++
which might confuse the reader. But in the end it is a matter of taste.