ing it repeatedly—for example, in a browser to change the color of an element every
half-second (we will learn more about browser code in
setInterval
(
function
() {
document
.
querySelector
(
'.rainbow'
)
.
style
[
'background-color'
]
=
getNextRainbowColor
();
},
500
);
This may not look so bad, and certainly the intent is clear: some HTML element with
the class
rainbow
will cycle through the colors of the rainbow. The problem is that if
something else calls
getNextRainbowColor()
, it will interfere with this code! This is
the point we should stop and question whether a function with side effects is a good
idea. In this case, an iterator would probably be a better choice:
function
getRainbowIterator
() {
const
colors
=
[
'red'
,
'orange'
,
'yellow'
,
'green'
,
'blue'
,
'indigo'
,
'violet'
];
let
colorIndex
=
-
1
;
return
{
next
() {
if
(
++
colorIndex
>=
colors
.
length
)
colorIndex
=
0
;
return
{
value
:
colors
[
colorIndex
],
done
:
false
};
}
};
}
Our function
getRainbowIterator
is now a pure function: it returns the same thing
every time (an iterator), and it has no side effects. We have to use it differently, but it’s
much safer:
const
rainbowIterator
=
getRainbowIterator
();
setInterval
(
function
() {
document
.
querySelector
(
'.rainbow'
)
.
style
[
'background-color'
]
=
rainbowIterator
.
next
().
value
;
},
500
);
You may be thinking that we’ve just kicked the can down the road: isn’t the
next()
method returning a different value every time? It is, but remember that
next()
is a
method, not a function. It operates in the context of the object to which it belongs, so
its behavior is controlled by that object. If we use
getRainbowIterator
in other parts
of our program, they will produce different iterators, which will not interfere with
any other iterator.
So What?
Now that we’ve seen three different hats that a function can wear (subroutine, sub‐
routine with return value, and pure function), we pause and ask ourselves “So what?”
Why do these distinctions matter?
So What? | 187