AlgoInsight

Free lesson · JavaScript Basic to Advance

Interview Lens: Modern JavaScript Patterns

Easy5 min read

The Question

Modern JavaScript interview questions cluster around fluency:

"Refactor this verbose code using modern features."

"What's the difference between || and ??? Give an example where they produce different results."

"When would you use a Map instead of an Object?"

"Destructure this function signature with sensible defaults."

"Code review this snippet — what would you change to make it modern?"

These come up at every interview level. They test fluency — whether modern JavaScript feels native to you or whether you still write ES5-style code from habit. Senior interviewers also use them to gauge how recently you've kept up with the language.

I'm Olena. Modern JavaScript fluency was where I struggled the most when I was self-taught from freeCodeCamp in 2018. The exercises showed me modern syntax, but my muscle memory kept pulling me back to ES5 patterns I'd seen in older tutorials. Closing that gap took deliberate practice. This Topic is the consolidated interview prep for that material, with Arun joining for the interview-strategy framing.

Why They Ask It

Modern JavaScript questions test three things at once. They test whether you've kept up with the language. They test whether your code style matches modern conventions. And they test design judgment — knowing when each modern feature applies and when an older approach is still better.

Three signals interviewers listen for:

Signal 1: Do you reach for modern features naturally? Faced with "merge these two objects," do you reach for { ...a, ...b } instantly, or do you start with Object.assign({}, a, b) (works but ES5-style), or worse, a manual loop? The reflex matters.

Signal 2: Do you know which feature to pick when? Multiple modern features can solve the same problem with different trade-offs. || vs ??. Object vs Map. Spread vs Array.from. Destructuring vs explicit access. Knowing which one fits a specific case is the senior skill.

Signal 3: Can you spot anti-patterns in older code? Code review questions test whether you can identify ES5-style patterns and refactor them. Manual for loops where map/filter would work. Verbose property access where destructuring would help. || where ?? is correct. These are the patterns interviewers cycle through.

I (Arun) noticed during my fourteen rejections that the interviews with code review components were where my outdated habits showed most clearly. I'd write var for variables, use function declarations where arrows would be cleaner, do manual property access where destructuring would read better. None of these were wrong. But they signaled I was carrying old habits forward, and interviewers wanted current fluency.

The Wrong Answers

Warning: The modern JavaScript mistakes interviewers see most often.

Using || where ?? is correct. The number-of-retries example from Topic 10.3: config.retries || 3 silently turns 0 into 3. The senior answer reaches for ?? for default values, reserves || for genuine truthiness checks.

Mutating instead of using spread. Code that does arr.push(item) and then continues to use arr is fine, but in the context of state management (React, Redux) it's wrong. Modern style produces new arrays with [...arr, item]. Interviewers look for this in any state-management discussion.

Using var. Topic 3.1 covered this. Modern code uses const and let. Reaching for var signals stale habits.

Using function expressions where arrows would be cleaner. Topic 5.3 covered arrow functions. For callbacks especially — arr.map(item => item.x) — arrows are the modern default. Using function here looks dated.

Manually iterating with for loops when array methods work. Manual for loops for transforming arrays signal pre-ES6 habits. Modern code uses map, filter, reduce for these cases.

Forgetting destructuring in function signatures. Functions that take an options object and then unpack each property with options.x line by line look verbose. Destructuring in the signature reads better.

Interactive animation: The four rows they always project - || against ??, a shallow spread copy, and a default that meets null.

The Right Answer

Three layers depending on what's being asked.

The 30-Second Answer (for "what's different about modern JavaScript")

"Modern JavaScript replaced verbose patterns with cleaner syntax across most areas. Spread and rest replaced Object.assign, concat, and manual argument collection. Destructuring replaced explicit property access. Optional chaining and nullish coalescing replaced repetitive guard checks. async/await replaced Promise chains and callback hell. Block-scoped let/const replaced function-scoped var. Modules replaced global namespace pollution. Most modern code uses all of these together, and the resulting style is significantly shorter and clearer than ES5 code."

The 2-Minute Answer

Build on the above:

"The shift wasn't just syntax — it was a philosophy change. Modern JavaScript emphasizes immutability, explicit handling of missing values, and declarative transformations over imperative loops. Spread produces new arrays and objects instead of mutating in place. Nullish coalescing distinguishes 'genuinely missing' from 'falsy'. Optional chaining acknowledges that data shapes can be incomplete without making code defensive at every step.

The most common refactor patterns in code review: replacing arr.push(item) followed by re-use with [...arr, item] to avoid mutation; replacing obj.key || default with obj.key ?? default for default values; replacing chained && guards with ?.; replacing manual for loops with map/filter/reduce; replacing object property access with destructuring in function signatures.

For interviews, the test isn't whether you can describe modern features — it's whether you reach for them automatically. Code that mixes styles, with some modern syntax and some ES5 patterns, signals incomplete adoption. Code that uses modern features consistently signals fluency."

The Deep Dive

For senior or staff interviews:

"Beyond syntax, modern JavaScript also brought type-aware features. Map and Set replaced patterns where developers had been misusing objects and arrays. WeakMap and WeakSet enabled memory-aware caching. Symbols enabled extension of language behavior without name collisions. Generators and async iteration enabled streaming patterns.

The harder design decisions in modern code are about when to use which feature. Spread is shallow — deep copies need structuredClone or libraries. Destructuring can become illegible at three levels of nesting. Optional chaining can hide bugs by silently returning undefined when a real error would have been better. Map is better than Object for non-string keys but loses JSON-friendliness.

The senior judgment is knowing the trade-offs. Reaching for ?. everywhere isn't modern fluency — it's defensive coding that hides bugs. Using Map for every key-value collection ignores the JSON-and-frameworks-expect-objects reality. The senior level is applying modern features deliberately, knowing why each one was chosen."

Interactive animation: Which container? Map, Set, a spread merge or WeakMap - each choice defended in one breath.

The Refactor Walk-Through

A common interview question: given old code, refactor it to modern style. Here's a representative example:

// Original (ES5-ish style)
function processUsers(users, filters) {
  filters = filters || {};
  var minAge = filters.minAge || 0;
  var role = filters.role || "any";
  var result = [];

  for (var i = 0; i < users.length; i++) {
    var user = users[i];
    if (user && user.age && user.age >= minAge) {
      if (role === "any" || (user.role && user.role === role)) {
        result.push({
          name: user.name,
          age: user.age,
          role: user.role
        });
      }
    }
  }
  return result;
}

// Modern refactor
function processUsersModern(users, { minAge = 0, role = "any" } = {}) {
  return users
    .filter(u => u?.age >= minAge)
    .filter(u => role === "any" || u?.role === role)
    .map(({ name, age, role }) => ({ name, age, role }));
}

const sample = [
  { name: "Olena", age: 35, role: "Head of Product" },
  { name: "Nikhil", age: 32, role: "Engineer" },
  { name: "Junior", age: 22, role: "Engineer" }
];

console.log(processUsers(sample, { minAge: 30, role: "Engineer" }));
console.log(processUsersModern(sample, { minAge: 30, role: "Engineer" }));

Walk through what changed:

The result is roughly half the length, reads more linearly, and shows modern fluency. This kind of refactor is what code review interviews test.

Interactive animation: The 2014 block, modernised live - destructured parameters with defaults, spread, ?. and ?? replace the old patterns.

The Follow-Ups

Interview: What the interviewer asks next.

"What's the difference between Object.assign and { ...obj }?" Both shallow copy. Spread is more readable. Object.assign supports getters/setters slightly differently in some edge cases.

"When is ?. not what you want?" When a missing value indicates a bug. Silently returning undefined hides the bug instead of throwing.

"What's a shallow copy versus a deep copy?" Shallow copy duplicates the outer level only. Deep copy duplicates everything recursively. For deep copies, use structuredClone (native, modern) or a library.

"How would you remove duplicates from an array?" [...new Set(arr)] is the canonical modern idiom. For arrays of objects (where reference equality won't dedupe), you'd use a Map keyed by the dedup criterion.

"Why use Map over Object for caches?" Predictable iteration order, real size property, non-string keys, no risk of prototype-key collision, faster for frequent additions and deletions.

The Founder Moment

Olena here. When I started teaching myself JavaScript in 2018, I was learning from a mix of resources — some current, some years old. I'd internalize a pattern from one tutorial, then see different syntax in another and not know which was preferred.

My first technical interview at a London startup, in 2019, asked me to do a code review on a 30-line function. The original was ES5-style — var everywhere, manual loops, verbose property access, function callbacks. The interviewer asked what I'd change.

I knew modern features existed. I'd used const, let, arrow functions, even destructuring sometimes. But under pressure, I couldn't articulate a philosophy for the refactor. I made some changes — switched var to const, added a few arrow functions — but I missed the bigger patterns. I missed destructuring the function parameter. I missed replacing the loop with .filter().map(). I missed ?? (which had just become standard).

I got rejected. The feedback the recruiter shared was generous but pointed: "shows familiarity with modern JavaScript but doesn't yet use it consistently."

I spent the next three months doing one exercise per day: take a piece of old JavaScript code, rewrite it in modern style. Sometimes my own old code. Sometimes Stack Overflow answers. Sometimes deliberately verbose examples I'd seek out. After three months, the modern style was automatic. The next interview I took, the same kind of refactor question, I aced.

What I took from that: modern JavaScript isn't about knowing the features. It's about using them by default. The features are easy. The reflex takes practice. The way to build the reflex is volume — refactor old code over and over until the modern way becomes the first thing you reach for.

If you're learning modern JavaScript now, do the same thing. Find old code. Refactor it. Many times. The fluency comes from the repetition, not from reading about the features.

Back to JavaScript Basic to Advance