EverProduct
Frontend

Stage 03 · The Language

The Conveyor: Transforming Data Instead of Commanding It

Nearly all frontend work is one shape: a list of objects arrives from a server and has to become something on screen. Learn to describe that transformation as a pipeline and most of your code stops being bookkeeping.

Take the same small task twice. You have a list of orders and you need the total of the paid ones.

Written as a loop, you create an accumulator, set up a counter, check a bound, increment, index into the array, test a condition, add to the accumulator, and finally return it. Eight decisions, of which one is about orders and seven are about running a loop correctly.

Written as a pipeline, you say: keep the paid ones, take their amounts, add them up. Three steps, all of them about orders.

Both work. The difference is what you'll be able to read a year from now, and how many places a typo can hide.

A loop says how. A pipeline says what. The second is still readable a year later — which is when it matters.

The shape of frontend data

Almost everything you'll handle has the same shape: an array of objects. A list of products, of messages, of users, of rows — arriving as JSON from a server, being filtered, sorted, grouped and rendered.

Which means the skill isn't "knowing JavaScript." It's fluency in a dozen operations on arrays of objects, plus knowing which one a situation needs. That fluency is worth more than any framework you'll learn on top of it, and unlike the framework, it doesn't expire.

The dozen operations

Each of these takes a function and applies it to every element. Learn what each returns — that's the part that decides which one you want:

  • map — same length, each element transformed. Objects to names, prices to formatted strings, data to elements on screen. The most used method in frontend, by a distance.
  • filter — a shorter array, keeping the ones that passed the test.
  • find — the first matching element itself, or undefined. When you want the thing, not a list containing it.
  • some / every — a boolean: does any / do all pass.
  • reduce — everything folded into one value: a sum, a maximum, an object grouped by key. Powerful, and easy to write in a way nobody can read. If a reduce needs a comment, a loop was probably clearer.
  • sort — ordered. Two traps here, both of which will bite you: it mutates the original array, and by default it compares elements as strings, so [10, 9, 1] sorts to [1, 10, 9]. Always pass a comparison function for numbers.
  • slice — a section, without touching the original. Not to be confused with splice, which cuts the original apart.
  • includes — is this value in there.
  • flat / flatMap — flatten nesting, and map-then-flatten in one pass.
  • Object.keys / values / entries — turn an object into arrays so the methods above apply to it too.

They chain, and a chain reads as a sentence: filter, then map, then sort. Each step hands its result to the next, and each step is independently understandable — which is why a broken pipeline is easy to debug and a broken twenty-line loop isn't.

Mutation: the split that causes real bugs

Two categories of method, and confusing them produces bugs that are genuinely hard to see.

Methods that change the original: push, pop, shift, unshift, splice, sort, reverse, and assigning to a property.

Methods that return something new: map, filter, slice, concat, flat, and the newer toSorted, toReversed and with, which exist precisely because the mutating versions were a design mistake.

Combine this with the reference rule from the previous article and you get the classic disaster: you "copy" an array with an assignment, sort the copy, and the original is now sorted too, because there never was a copy. Somewhere a list re-renders in the wrong order and nothing in the stack trace points at the sort.

Copying properly is done with spread — for arrays and objects alike — and there's a limit you must know about: spread makes a shallow copy. The top level is new; the nested objects inside are still shared. For genuinely nested data, either copy each level you change or use structuredClone.

There's a frontend-specific reason to care beyond tidiness. Frameworks decide what to redraw by comparing references: if the array is the same object it was before, it's assumed nothing changed, however different its contents are. Mutating your data and wondering why the screen didn't update is a rite of passage, and this paragraph is the reason. Two articles from now it'll be a rule; here it's the mechanism.

The syntax that removes the noise

A handful of modern features do most of the tidying, and they're worth learning as a set because they appear in every codebase you'll ever open.

Destructuring pulls fields out of an object or array into named variables in one line, with defaults for what's missing. It's how function parameters are usually received in modern code.

Spread and rest — the same three dots doing two jobs. Spreading unpacks an array or object into another one, which is how copies and merges are made. Resting collects the leftovers, which is how a function takes an unknown number of arguments.

Optional chaininguser?.profile?.city — returns undefined instead of throwing when something along the path is missing. It replaces the chains of && that used to fill API-handling code.

Nullish coalescing?? — supplies a fallback only when the value is null or undefined. This is not the same as ||, and the difference is a real bug: count || 10 replaces a legitimate 0 with 10, and "" with your placeholder. Use || for "falsy", ?? for "absent" — and in practice you almost always mean absent.

Functions are values

The methods above take functions as arguments, which is only possible because a function in JavaScript is a value like any other: storable, passable, returnable.

Two properties make a function pleasant to work with. It returns a result rather than modifying something outside itself, and it depends only on its arguments rather than on the surrounding state. Such a function is testable in isolation, reusable without surprises, and readable without knowing anything else — and it's the shape most of your logic should have. Reserve side effects — writing to the DOM, calling a server, changing global state — for a few identified places rather than sprinkling them everywhere.

Two small habits matter more than they look. Name functions for what they return, not how they work: getVisibleItems beats processData, and a good name means the reader doesn't have to open the body. And return early: check the awkward cases at the top and get them out of the way, so the main path isn't buried under three levels of nesting.

Practise choosing, not applying

This is the exact situation the How to Learn sphere's key ring article describes. Twenty exercises in a row on reduce train you to apply reduce — but in real work nobody labels the problem, and the actual skill is picking the right key off the ring.

So practise the choice separately from the solution. Take ten realistic problems — "the three most expensive items", "does anyone have an unpaid invoice", "count of orders per city", "the same list, sorted by date" — and for each, without writing a line, just name the method and why. Five minutes, and it trains the move that textbooks skip.

Two more things that pay off here. Space the repeats: work through the same set of transformations again three days later from a blank file rather than pushing on the same day, because the struggle to recall is what builds the block. And build the chunk deliberately: after a few weeks, "group a list by a field and sort each group" should be a single thought, not a fifteen-minute exploration. That fusion is the whole difference between reading JavaScript and writing it.

In practice

Rewrite three of your own loops as pipelines. Then read both versions aloud. The comparison teaches more than an article can.

Say the return type out loud before choosing a method. "I need one element" — find. "I need a shorter list" — filter. "I need the same length, transformed" — map. Most wrong choices are answered by that one question.

Ban mutating methods for a week. toSorted instead of sort, spread instead of push. You'll feel where mutation was load-bearing, and you'll stop being surprised by shared references.

Chain in three steps, then stop. A pipeline longer than that usually wants an intermediate variable with a name that explains what it holds.

Read a real API response and describe the transformation in words before writing code: "keep the active ones, take name and total, sort by total descending." The sentence is the pipeline; then you just type it.

Check yourself

Close the article and answer in your own words:

  1. What single data shape covers most frontend work?
  2. What does each of map, filter, find, some and reduce return?
  3. Name three methods that mutate and three that return something new.
  4. Why can sorting an array break a screen you never touched?
  5. What is a shallow copy, and when does it stop being enough?
  6. What's the difference between || and ??, and which bug does it cause?
  7. Why is practising the choice of method different from practising the method?

In short

  • Frontend data is nearly always an array of objects, and the job is describing its transformation rather than administering a loop.
  • Choose methods by what they return: map for same-length transforms, filter for fewer, find for one, some/every for a boolean, reduce for a fold.
  • sort mutates and compares as strings by default — pass a comparator, or use toSorted.
  • Mutating methods versus new-value methods is the split that causes invisible bugs, especially combined with references.
  • Spread copies shallowly; nested data needs deeper copying, and frameworks detect change by reference, so mutation silently skips re-rendering.
  • Destructuring, spread/rest, ?. and ?? remove most of the noise from real code — and ?? means absent, not falsy.
  • Functions are values; prefer ones that return rather than modify, name them for what they return, and return early.
  • Practise choosing the method, spaced and interleaved, until common transformations become a single thought.