Rethinking JavaScript: Death for
- Transfer
The cycle
for
served us well, but it is outdated and should give way to newer, more functional programming techniques. Fortunately, this fact does not require you to be a master of functional programming, moreover, this is something that you can use in your current projects today !
So what is the problem of the for loop in JavaScript?
The design of the cycle
for
encourages mutations of state ( Eng. Mutation of state - change of state - approx. Translator ) and the use of side effects ( Eng. Side effects - side effects - approx. Translator), which are potential sources of bugs and unpredictable code behavior. We have all heard that a global state is bad, and that we must avoid it. However, the local state shares the same problems as the global one , we simply do not encounter them so often, because they appear on a smaller scale. In fact, we never solved the problem, but simply minimized it.
One day, using mutable state ( Eng. Mutable state - variable state - approx interpreter. ), The value of the random variable is changed for an unknown reason, and you will spend hours debugging and search for reasons for the change. My head hair stands on end, just from the thought of it.
I would like to talk a little about side effects . These words even sound awful, side effects. Rubbish. Do you want side effects in your programs? No, I do not want side effects in my programs!
But what are side effects?
It is believed that a function has side effects if it modifies something outside its scope. This can be a variable change, user input, a request to the api, writing data to disk, a log to the console, etc.
Side effects are a really powerful tool, but with great power comes a lot of responsibility.
Side effects, if possible, should be eliminated or at least encapsulated so that we can control them. Functions with side effects are harder to read and test.Avoid them whenever possible. Fortunately, in this article, we will not worry about side effects.
Less words, more code. Let's look at a typical cycle
for
that you probably saw hundreds of times.const cats = [
{ name: 'Mojo', months: 84 },
{ name: 'Mao-Mao', months: 34 },
{ name: 'Waffles', months: 4 },
{ name: 'Pickles', months: 6 }
]
var kittens = []
// Типичный, плохо написанный цикл for
for (var i = 0; i < cats.length; i++) {
if (cats[i].months < 7) {
kittens.push(cats[i].name)
}
}
console.log(kittens)
I’m going to refactor this code step by step so that you can observe how easy it is to turn your own code into something more beautiful.
First, I will extract the conditional expression into a separate function:
const isKitten = cat => cat.months < 7
var kittens = []
for (var i = 0; i < cats.length; i++) {
if (isKitten(cats[i])) {
kittens.push(cats[i].name)
}
}
Making conditions is generally good practice. Changing the filtration from “less than 7 months” to “is a kitten” is a big step forward. Now the code conveys our intentions much better. Why do we take cats up to 7 months? Not quite clear. We want to find kittens, so let the code say this!
Another benefit is that
isKitten
you can now reuse it, and we all know that reusing code should always be our goal . The next change is to extract the conversion (or mapping) of the cat to its name. This change will be clearer later, but for now, just trust me.
const isKitten = cat => cat.months < 7
const getName = cat => cat.name
var kittens = []
for (var i = 0; i < cats.length; i++) {
if (isKitten(cats[i])) {
kittens.push(getName(cats[i]))
}
}
I was going to write a few paragraphs to describe the mechanics of work
filter
and map
, but instead, I will show you how easy it is to read and understand this code, even when I see them ( filter
and map
- approx. Translator ) for the first time. This is the best demonstration of how readable your code can become.const isKitten = cat => cat.months < 7
const getName = cat => cat.name
const kittens =
cats.filter(isKitten)
.map(getName)
Please note we got rid of
kittens.push(...)
. No more state mutations and none var
.Code favoring const
front var
and let
looks pretty darn attractive
Of course, we could use it
const
from the very beginning, since it does not make the object itself immutable (more on that another time), but this is an invented example, do not bother! And the last change, I would also suggest extracting filtering and mapping into a function (to completely reuse the code).
And all together:
const isKitten = cat => cat.months < 7
const getName = cat => cat.name
const getKittenNames = cats =>
cats.filter(isKitten)
.map(getName)
const cats = [
{ name: 'Mojo', months: 84 },
{ name: 'Mao-Mao', months: 34 },
{ name: 'Waffles', months: 4 },
{ name: 'Pickles', months: 6 }
]
const kittens = getKittenNames(cats)
console.log(kittens)
Homework
Examine the extraction of filter and map methods from their objects. Task with an asterisk: explore the composition of functions.
- Functional JavaScript: Decoupling methods from their objects
- Functional JavaScript: Function Composition For Every Day Use.
What about break
Many of you have been asking, “What about,
break
” see part two of the series “Rethinking JavaScript: break is GOTO loops.”Conclusion
Write what you think about this in the comments. Is the cycle dead for you
for
? This is a trifle for you, but it makes me very happy when someone follows me on a medium or on Twitter ( @joelnet ), and if you find my story useless, tell us about it in the comments.
Thanks!