Modern JavaScript evolves feature by feature rather than as one dependable ES7-plus runtime label. New access, collection, class, module, Promise, and immutable-update capabilities must be evaluated against exact runtime targets.
Adopt features for clearer semantics, distinguish syntax transforms from built-in polyfills, review codemods for behavioral changes, and remove compatibility layers as support targets advance.
Modern ECMAScript provides focused syntax and APIs for asynchronous work, null-safe access, immutable collection updates, grouping, iteration, modules, numeric values, strings, sets, and regular expressions.
Select features one by one from the minimum browser and server runtimes your application supports. Syntax may require transpilation, while missing built-in APIs require a polyfill, alternative implementation, or higher runtime baseline.
Use the table as a selection map: find the problem category, choose the smallest suitable feature, and verify support for that feature rather than assuming one edition label guarantees the whole runtime.
| Problem | Useful Features | Selection Rule |
|---|---|---|
| Membership and powers | Array.includes(), exponentiation operator ** | Use includes for SameValueZero membership and ** for numeric exponentiation |
| Asynchronous control flow | async/await, Promise.finally(), Promise.allSettled(), Promise.any() | Choose concurrency behavior and handle every rejection path explicitly |
| Object transformation | Object.entries(), Object.fromEntries(), object rest and spread | Use explicit copying and remember nested objects remain shared |
| Null-safe access | Optional chaining and nullish coalescing | Preserve valid falsy values and avoid hiding required missing data |
| Immutable arrays | toSorted(), toReversed(), toSpliced(), with(), findLast() | Use copy methods when callers must retain the original array |
| Grouping and sets | Object.groupBy(), Map.groupBy(), Set composition methods | Choose object or map keys from the required key type and identity semantics |
| Iteration | Async iteration, iterator helpers, Array.fromAsync() | Keep lazy and one-pass behavior visible and bound collected output |
| Modules | Dynamic import, top-level await, import attributes, JSON modules | Match runtime module support, loader rules, and deployment output |
| Errors and regular expressions | Error.cause, RegExp.escape(), modern match indices and modifiers | Preserve causes and verify regex flags on minimum runtimes |
| Numeric and binary data | BigInt, numeric separators, Float16Array where supported | Choose from precision, serialization, typed-array, and interoperability requirements |
Array.includes() checks whether an array contains a value using SameValueZero comparison. The exponentiation operator `**` raises a number to a power and should be parenthesized when precedence could be unclear.
const roles = ["admin", "editor", "viewer"];
console.log(roles.includes("editor")); // true
console.log(roles.includes("owner")); // false
console.log(2 ** 3); // 8
console.log(Math.pow(2, 3)); // 8
ES2017 added async and await, which make promise-based code read like synchronous code. An async function always returns a promise, and await pauses inside that function until the promise settles.
async function loadUser(id) {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
loadUser(7)
.then(user => console.log(user.name))
.catch(error => console.error(error.message));
Object.values() returns an array of object values. Object.entries() returns key-value pairs. ES2019 added Object.fromEntries(), which converts key-value pairs back into an object.
const scores = {
Asha: 92,
Rohan: 78,
Meera: 88
};
console.log(Object.values(scores)); // [92, 78, 88]
console.log(Object.entries(scores)); // [["Asha", 92], ["Rohan", 78], ["Meera", 88]]
const passedEntries = Object.entries(scores)
.filter(([name, score]) => score >= 80);
const passed = Object.fromEntries(passedEntries);
console.log(passed); // { Asha: 92, Meera: 88 }
ES2017 added padStart() and padEnd() for formatting strings. ES2021 added replaceAll() for replacing every occurrence of a string pattern.
const invoiceNumber = "42";
console.log(invoiceNumber.padStart(5, "0")); // 00042
console.log("Total".padEnd(10, ".")); // Total.....
const slug = "learn javascript javascript".replaceAll(" ", "-");
console.log(slug); // learn-javascript-javascript
ES2018 brought rest/spread syntax to objects. Object rest collects remaining properties, while object spread copies or merges properties into a new object.
const user = {
id: 101,
name: "Nisha",
password: "secret",
role: "admin"
};
const { password, ...publicUser } = user;
console.log(publicUser); // { id: 101, name: "Nisha", role: "admin" }
const updatedUser = {
...publicUser,
role: "editor",
active: true
};
console.log(updatedUser);
Modern JavaScript added several promise helpers. finally() runs cleanup after success or failure. allSettled() waits for every promise. any() resolves when the first promise fulfills. withResolvers() creates a promise with exposed resolve and reject functions. Promise.try() wraps sync or async work in a promise.
const fast = Promise.resolve("fast result");
const slow = new Promise(resolve => setTimeout(() => resolve("slow result"), 1000));
const failed = Promise.reject(new Error("failed"));
Promise.allSettled([fast, slow, failed]).then(results => {
results.forEach(result => console.log(result.status));
});
Promise.any([failed, slow, fast])
.then(value => console.log(value))
.finally(() => console.log("cleanup"));
ES2019 added Array.prototype.flat() and flatMap(). Use flat() to flatten nested arrays and flatMap() when each item should produce zero, one, or many output items.
const nested = [1, [2, 3], [4, [5]]];
console.log(nested.flat()); // [1, 2, 3, 4, [5]]
console.log(nested.flat(2)); // [1, 2, 3, 4, 5]
const sentences = ["learn js", "write code"];
const words = sentences.flatMap(sentence => sentence.split(" "));
console.log(words); // ["learn", "js", "write", "code"]
Optional chaining ?. safely reads nested properties or calls functions when part of the chain may be null or undefined. If a value is missing, JavaScript returns undefined instead of throwing an error.
const user = {
name: "Ravi",
profile: {
social: {
twitter: "@ravi"
}
}
};
console.log(user.profile?.social?.twitter); // @ravi
console.log(user.settings?.theme); // undefined
user.notify?.("Welcome"); // calls only if notify exists
The nullish coalescing operator ?? provides a fallback only when the left side is null or undefined. It does not replace valid falsy values such as 0, false, or an empty string.
const settings = {
volume: 0,
darkMode: false
};
console.log(settings.volume ?? 50); // 0
console.log(settings.darkMode ?? true); // false
console.log(settings.language ?? "en"); // en
BigInt represents integers larger than the safe Number limit. Numeric separators use underscores to make large numbers easier to read.
const safeNumber = Number.MAX_SAFE_INTEGER;
const largeNumber = 9_007_199_254_740_991n;
console.log(safeNumber);
console.log(largeNumber + 10n);
const price = 1_50_000;
console.log(price); // 150000
ES2021 added logical assignment operators. They combine logical checks with assignment and are useful for setting defaults or updating values only under certain conditions.
const config = {
retries: 0,
title: ""
};
config.retries ??= 3; // keeps 0 because it is not null or undefined
config.title ||= "Untitled"; // replaces empty string because it is falsy
config.enabled &&= true; // assigns only if enabled is truthy
console.log(config);
at() supports positive and negative indexes. ES2023 added findLast() and findLastIndex(). It also added copy methods such as toSorted(), toReversed(), toSpliced(), and with(), which return new arrays instead of mutating the original.
const numbers = [4, 8, 15, 16, 23, 42];
console.log(numbers.at(-1)); // 42
console.log(numbers.findLast(number => number < 20)); // 16
const sorted = numbers.toSorted((a, b) => a - b);
const changed = numbers.with(0, 99);
console.log(numbers); // original array is unchanged
console.log(sorted);
console.log(changed);
ES2022 improved classes with public fields, private fields, static blocks, and better error metadata. Private fields start with # and can only be accessed inside the class body.
class Counter {
#value = 0;
increment() {
this.#value++;
return this.#value;
}
get value() {
return this.#value;
}
}
const counter = new Counter();
console.log(counter.increment()); // 1
console.log(counter.value); // 1
Top-level await lets ES modules wait for async work without wrapping everything in an async function. JSON modules and import attributes make it possible to import JSON data as a module in supported environments.
// settings.json
// { "theme": "dark", "language": "en" }
import settings from "./settings.json" with { type: "json" };
const response = await fetch("/api/current-user");
const currentUser = await response.json();
console.log(settings.theme);
console.log(currentUser.name);
ES2024 added grouping helpers. Object.groupBy() groups values into a plain object. Map.groupBy() groups values into a Map, which is useful when group keys are not simple strings.
const tasks = [
{ title: "Write tutorial", status: "done" },
{ title: "Record video", status: "pending" },
{ title: "Publish page", status: "done" }
];
const grouped = Object.groupBy(tasks, task => task.status);
console.log(grouped.done);
console.log(grouped.pending);
ES2025 added built-in set operations. These methods make mathematical set logic much easier to read than manual loops and filters.
const frontend = new Set(["html", "css", "javascript"]);
const backend = new Set(["node", "javascript", "sql"]);
console.log(frontend.union(backend));
console.log(frontend.intersection(backend));
console.log(frontend.difference(backend));
console.log(frontend.isDisjointFrom(backend)); // false
ES2025 introduced iterator helpers such as map(), filter(), take(), drop(), and toArray() on iterators. Unlike array methods, iterator helpers are lazy: they process values as needed instead of creating intermediate arrays at every step.
const numbers = [1, 2, 3, 4, 5, 6].values();
const result = numbers
.filter(number => number % 2 === 0)
.map(number => number * 10)
.take(2)
.toArray();
console.log(result); // [20, 40]
RegExp.escape() safely escapes user-provided text before placing it inside a regular expression. This prevents special characters from accidentally changing the pattern.
const userSearch = "price (USD)?";
const safePattern = RegExp.escape(userSearch);
const regex = new RegExp(safePattern, "i");
console.log(regex.test("Price (USD)?")); // true
ES7+ is not one single release. It is the steady evolution of JavaScript after ES6. The most important idea is to learn the features that improve real code: safer property access, clearer async handling, immutable array updates, better data grouping, stronger collection tools, and cleaner modules.
When you use newer ECMAScript features, always check your target environment. A feature may be standardized but still require a recent browser, Node.js version, transpiler, or polyfill in production.
Optional chaining and nullish coalescing make intentional absence concise without replacing valid zero, false, or empty-string values. Logical assignment extends those conditions to assignment. These features improve contracts only when required data is still validated rather than silently skipped.
Relative indexing with `at`, last-match methods, and copying array methods such as `toSorted`, `toReversed`, `toSpliced`, and `with` express common operations without mutating the source. Their copies remain shallow, and compatibility must match declared runtime targets.
Object grouping and newer collection helpers can reduce boilerplate, but key type, collision policy, ordering, and result container still matter. Evaluate the actual method contract and avoid a polyfill whose performance or edge behavior differs from supported native implementations.
Static private fields, top-level await, import metadata, and newer module capabilities affect initialization and deployment. Top-level await can delay dependent modules and amplify cycles, so keep startup dependencies small and make asynchronous bootstrap visible.
Newer Promise helpers can expose resolver functions, select first fulfillment, or collect settlement results, but they do not create cancellation or stop losing operations. Keep operation factories, AbortSignals, concurrency limits, and error ownership explicit around every composition method.
Explicit resource-management syntax and disposable stacks can make cleanup ordering visible where supported. Adoption still requires resource objects with correct idempotent disposal, clear asynchronous behavior, and compatible tooling. A language construct cannot repair a resource whose close operation leaks or hides failure.
Feature availability depends on the exact runtime, not a broad label such as ES7 or modern JavaScript. Maintain a browser and server support matrix, use compatibility data for each feature, and test the emitted artifact. Syntax support and built-in API support are separate concerns.
Transpilers can rewrite syntax but may require helpers or polyfills for new built-ins. Load only needed, compatible polyfills and understand whether they patch globals. A build that parses optional chaining does not automatically provide a newly introduced Array or Promise method.
Adopt features when they clarify ownership, failure, or data flow. Codemods need review for evaluation order, mutation, this binding, sparse collections, and async behavior. Keep regression tests around mechanical modernization and remove obsolete compatibility code after the minimum runtime advances.
ES7+ means ECMAScript 2016 and every newer yearly JavaScript edition after ES6 / ES2015.
Yes. ES6 introduced the foundation of modern JavaScript: let, const, arrow functions, classes, modules, destructuring, promises, maps, sets, and more.
No. Widely adopted features such as async/await and optional chaining are broadly supported, but newer features such as iterator helpers or Set methods may need newer runtimes or polyfills.
Explore 500+ free tutorials across 20+ languages and frameworks.