ES6+ Features
ES6 (ECMAScript 2015) and the annual editions since turned JavaScript from a language of functions and objects into one with full syntax for unpacking data, terse safe-access forms, and a standard module system. This is not sugar for looks — each construct has concrete mechanics: spread makes a shallow copy, ?? reacts only to nullish, import yields a live binding. Those edges separate a candidate who "saw the syntax" from one who understands what happens under the hood.
The topic gathers the features that appear in every file of modern code. The traps here are subtle and recur from interview to interview — ?? versus ||, spread versus rest, an optional parameter versus a default. Study each layer on its own; the full map is below.
Topic map
- Destructuring — unpacking arrays by position and objects by name in one expression, down to nesting, aliasing, and swapping variables.
- Rest and spread — the
...operator gathers the remainder into one array (rest) or expands a source into many elements (spread), making a shallow copy. - Default parameters — a parameter's fallback value, firing strictly on
undefinedand evaluated lazily per call. - for...of and for...in — iterating the values of an iterable versus the enumerable keys of any object.
- Optional chaining
?.— safe access that short-circuits toundefinedon a nullish reference instead of throwing. - Nullish coalescing
??— supplying a default only onnull/undefined, unlike||, which reacts to any falsy value. - ES6 classes —
class,constructor,extends/super,static, and private#fieldsas syntax over prototypes. - ES modules —
import/export, a private module scope, live read-only bindings, and automatic strict mode. - Dynamic
import()— loading a module at runtime, returning a promise and enabling code splitting and lazy loading.
Common traps
| Mistake | Consequence | ||||
|---|---|---|---|---|---|
| Thinking array destructuring binds by name | In fact [a, b] binds by position while {x, y} binds by property name | ||||
Thinking {x: y} binds x | The binding is the right-hand name y; x is only the source | ||||
| Confusing spread (one → many) with rest (many → one) | Wrong meaning of ... in a given position | ||||
Treating {...obj} as a deep clone | It is a shallow copy — nested objects are shared by reference | ||||
| Believing a default fires on any falsy value | A default applies only on undefined; 0, '', null suppress it | ||||
Using for...in on an array for values | It yields key strings and inherited properties, not elements | ||||
Confusing ?? with ` | ` | ` | replaces a valid 0 or ''; ??` reacts only to nullish | ||
Thinking ?. guards the whole chain | It guards only the access directly in front of it | ||||
Calling a class without new | Throws a TypeError — a constructor cannot be called as a function | ||||
| Treating classes as "pure sugar" | Their methods are non-enumerable, the declaration is not hoisted, the body is always strict | ||||
Expecting import() to return the module directly | It returns a promise for the namespace object |
Interview relevance
ES6 is asked to separate candidates who apply syntax mechanically from those who understand the semantics. "?? is like || but prettier" fails the question; the right answer names the concrete edge: "?? reacts only to null and undefined, so it keeps a valid 0".
Typical checks:
- The difference between binding by position and by name in destructuring; the swap idiom
[a, b] = [b, a]. - The direction of
...: gathers (rest) or expands (spread), and that the copy is shallow. - The exact default trigger —
undefinedonly, not any falsy value. - How
for...ofdiffers fromfor...inon an array. - How
?./??differ from||and from "any falsy value". - That classes are syntax over prototypes, but with differences in enumerability, hoisting, and strict mode.
- What an ESM live binding provides and why
import()enables code splitting.
Common wrong answer: "{...obj} makes a deep copy, so source changes will not touch it". In fact spread copies only own enumerable properties one level deep; nested objects stay shared, and a mutation to a nested object is visible in both copies.