Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Comments on Does a for...of loop must contain a variable without assignment sign, and why?
Parent
Does a for...of loop must contain a variable without assignment sign, and why?
The following code pattern reflects this answer.
This code contains a for...of
loop:
for (const child of e.childNodes) {
// Do stuff;
}
Does a for...of
loop must contain a variable without assignment sign (=
), and why?
Is there a way to achieve the same result with "regular" variable declaration?
Post
Does a
for...of
loop must contain a variable without assignment sign (=
)
Yes.
and why?
Because that's the syntax defined by the language specification. The people who defined it decided to do it that way.
Unless those people answer here (or we find a discussion about it somewhere, which I couldn't), we can only speculate why they have chosen this particular syntax instead of any other one.
In the comments, there's one argument against having a =
: it would be confusing to have code like for (const variable = collection)
, because it looks like the whole collection
is being assigned to variable
. By making it for (const variable of collection)
, it avoids this confusion and makes it more clear that the variable is iterating through the collection
's elements.
Of course this is debatable and each one will have their opinion about this being more clear or confusing. When I learned
for..of
, I was already familiar with similar constructs from another languages, so it wasn't confusing to me. But YMMV, as always.
I'd like to add that other languages have similar loops:
- Java has the enhanced-for loop:
for (Type variable : collection)
- C# has the
foreach
loop:foreach (Type variable in collection)
- Python uses a
for..in
loop:for variable in collection
- PHP has the
foreach
loop, but the variable comes after the collection:foreach ($collection as $variable)
- D also has
foreach
, but with a different syntax:foreach (variable; collection)
And so on, the list of syntax variations among different languages is huge. Just for the record, JavaScript also has for..in
loops, with a similar syntax (for (const variable in collection)
), but different behaviour.
Anyway, each language chooses whatever syntax they want to. The people who defines it might consider many factors to decide between one or another, and each language also has their own processes to discuss and decide it (such as PEP's for Python, JEP's for Java, TC39 for JS, etc). Sometimes the decision has a reasonable rationale, technical justifications, etc, but sometimes is just a matter of personal preference (I can't tell which one drove the decision of the for..of
syntax, though).
Therefore, IMO the only reasonable answer to "Why does a for..of
loop requires a specific syntax?" is simply "Because it was defined that way". To know why it was decided to use syntax A instead of B, we should ask to the people who made the decision (without that, the best we can do is only speculate).
Is there a way to achieve the same result with "regular" variable declaration?
Maybe, it depends on what the code does.
For a simple loop such as:
const collection = [ 1, 2, 3, 4, 5 ];
for (const variable of collection) {
console.log(variable);
}
You could declare the variable outside the loop:
let variable;
for (variable of collection) {
console.log(variable);
}
Although the "result" (AKA the output) is the same, there are some differences.
I can't use const
in the second case, because the variable
will be assigned a different value in each iteration.
And by not using const
, this means that I could change its value inside the loop:
let variable;
for (variable of collection) {
variable = 1000;
console.log(variable);
}
If I use const
and declare it inside the loop, though, I can't change its value:
for (const variable of collection) {
variable = 1000; // error!
console.log(variable);
}
This gives me an error because I can't assign another value to a const
variable. That's useful if you want to make sure that the variable
isn't changed inside the loop.
Another difference is that in the second code, the variable
still exists after the loop:
let variable;
for (variable of collection) {
console.log(variable);
}
console.log('after', variable); // variable has the value of the last collection's element
That's because it was declared outside the loop.
But if I do this:
for (const variable of collection) {
console.log(variable);
}
console.log('after', variable); // error (variable is out of scope and doesn't exist outside the loop)
Now the variable
's scope is the for..of
loop, thus it can't be accessed outside the loop. That's useful to limit the variable's scope.
Of course you could also use a "traditional" for
:
for (let i = 0; i < collection.length; i++) {
const variable = collection[i];
console.log(variable);
}
But the for..of
loop is actually more than just an alternative syntax, because it allows you to loop through any iterable object.
Not all iterable objects have the length
property, and some might not support the []
syntax to access a specific element. For those objects, the for..of
loop provides a standard way to loop through them, all they have to do is implement the iterable protocol.
Just to provide some examples:
// A Set doesn't have a way to access individual elements by index
var set = new Set([1, 2, 3, 1, 4, 5]);
for (const n of set) {
console.log(n);
}
// I can iterate through the keys and respective values of a Map
var map = new Map([ ['name', 'Douglas'], ['age', 42], ['country', 'UK']]);
// and I can also use destructuring assignment: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
for (const [key, value] of map.entries()) {
console.log(`${key} = ${value}`);
}
And there are cases where the results are different, such as when looping through the characters of a string:
const string = 'abc' + String.fromCodePoint(0x1F60A);
for (const character of string) {
console.log(character);
}
The output is (note the emoji in the end):
a
b
c
😊
But with a traditional for
:
for (let i = 0; i < string.length; i++) {
console.log(string[i]);
}
This will print:
a
b
c
�
�
That's because for..of
loops through the Unicode code points, while the traditional loop breaks some of them in surrogate pairs. I know that's way beside the point, it was just to show that a for..of
loop has its own characteristics, and sometimes there's no obvious equivalent using other types of loops (but if you're curious about all this Unicode stuff, take a look at this answer).
Summary
- the
for..of
loop syntax is the way it is, because whoever designed it, decided it that way - some cases might be interchangeable with a traditional
for
loop, some other cases might not
1 comment thread