- Explain the difference between looping and iteration.
- Iterate over arrays with the
for...of
statement. - Enumerate an object's properties with the
for...in
statement.
When we create a for
loop to loop over an array, we base the loop's condition
off of the .length
of the array. This works, but it's a lot of syntactic cruft
to remember:
for (let i = 0; i < array.length; i++) {
// Loop body
}
The problem is that we're using a looping construct to perform iteration.
There's a pretty fine line separating the concepts of looping and iteration, and only the truly pedantic will call you out if you use one in place of the other.
Looping is the process of executing a set of statements repeatedly until a
condition is met. It's great for when we want to do something a specific
number of times (for
loop) or unlimited times until the condition is met
(while
or do while
loop).
Iteration is the process of executing a set of statements once for each
element in a collection. We can accomplish this with a for
loop:
let myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
for (let i = 0; i < myArray.length; i++) {
console.log(myArray[i]);
}
or with a while
loop:
let myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
let j = 0;
while (j < myArray.length) {
console.log(myArray[j++]);
}
but neither is very pretty. The for...of
statement gives us a better way.
Using for...of
, the code above becomes:
const myArray = ['a', 'b', 'c', 'd', 'e', 'f', 'g'];
for (const element of myArray) {
console.log(element);
}
Using a construct that is specifically meant for iteration results in much
cleaner code: there's no initialization of a counter, no condition, no
incrementing the counter, and no bracket notation to access elements in the
array (myArray[i]
).
As you might've noticed, for...of
allows us to use const
instead of let
.
In for
and while
statements, let
is required because we are incrementing a
counter variable. The incrementing process involves taking the counter's current
value, adding 1
to it, and then assigning that new value to the variable. That
reassignment precludes us from using our beloved const
, which cannot be
reassigned.
Delightfully, the for...of
statement involves no such reassignment. On each
trip into the loop body (which is a block — note the curly braces), we assign
the next element in the collection to a new element
variable. Upon
reaching the end of the block, the block-scoped variable vanishes, and we return
to the top. Then we repeat the process, assigning the next element in the
collection to a new element
variable.
A string is effectively an ordered collection (like an array) of characters,
which for...of
is more than happy to iterate over:
for (const char of 'Hello, world!') {
console.log(char);
}
// LOG: H
// LOG: e
// LOG: l
// LOG: l
// LOG: o
// LOG: ,
// LOG:
// LOG: w
// LOG: o
// LOG: r
// LOG: l
// LOG: d
// LOG: !
Use a for...of
statement anytime you want to iterate over an array.
The for...in
statement is similar to for...of
; it's generally used for
iterating over the properties in an object. The statement follows this syntax:
for (const [KEY] in [OBJECT]) {
// Code in the statement body
}
The for...in
statement iterates over the properties in an object, but it
doesn't pass the entire property into the block. Instead, it only passes in the
keys:
const address = {
street1: '11 Broadway',
street2: '2nd Floor',
city: 'New York',
state: 'NY',
zipCode: '10004',
};
for (const key in address) {
console.log(key);
}
// LOG: street1
// LOG: street2
// LOG: city
// LOG: state
// LOG: zipCode
Accessing the object's values is as simple as combining the passed-in key with the bracket operator:
const address = {
street1: '11 Broadway',
street2: '2nd Floor',
city: 'New York',
state: 'NY',
zipCode: "10004"
};
for (const key in address) {
console.log(address[key]);
}
// LOG: 11 Broadway
// LOG: 2nd Floor
// LOG: New York
// LOG: NY
// LOG: 10004
Can you think of why the bracket operator is required? Let's see what happens when we use the dot operator:
const address = {
street1: '11 Broadway',
street2: '2nd Floor',
city: 'New York',
state: 'NY',
zipCode: '10004'
};
for (const key in address) {
console.log(address.key);
}
// LOG: undefined
// LOG: undefined
// LOG: undefined
// LOG: undefined
// LOG: undefined
The for...in
statement iterates over the five properties in address
,
successively passing in the object's keys. However, inside the statement body
we're trying to access address.key
. If you recall from the lesson on objects,
variables don't work with the dot operator because it treats the variable name
as a literal key — that is, address.key
is trying to access the property
on address
with a key of key
. Since there is no key
property in address
,
it returns undefined
. To prove this, let's add a key
property to address
:
address.key = "Let's have a 'key' key!";
for (const key in address) {
console.log(address.key);
}
// LOG: Let's have a 'key' key!
// LOG: Let's have a 'key' key!
// LOG: Let's have a 'key' key!
// LOG: Let's have a 'key' key!
// LOG: Let's have a 'key' key!
// LOG: Let's have a 'key' key!
Use a for...in
statement whenever you want to enumerate the properties of an
object.
Because arrays are objects, for...in
will work with arrays. In fact,
because for...of
was added to JavaScript later than for...in
, you might see
older code that uses for...in
to iterate over arrays. However, as a general
rule, don't use for...in
with arrays. When iterating over an array, an
ordered collection, we would expect the elements in the array to be dealt
with in order. However, because of how for...in
works under the hood,
there's no guarantee of order. From the MDN documentation:
A
for...in
loop iterates over the properties of an object in an arbitrary order ... one cannot depend on the seeming orderliness of iteration, at least in a cross-browser setting.
What this means is that, with for...in
, different browsers might iterate over
the same object's properties in different orders. That's not cool! Cross-browser
consistency is very important. A lot of progress has been made towards
standardizing the behavior of for...in
across all major browsers, but there's
still no reason to use for...in
with arrays when we have the wonderfully
consistent for...of
tailor-made for the job.