ECMAScript 2026 (ES2026) to 17. wydanie standardu JavaScript, zatwierdzone 30 czerwca 2026 roku. Dodaje siedem nowości, m.in.Array.fromAsync,Error.isError,Math.sumPrecise,Map.getOrInsert,Iterator.concat, konwersjęUint8Arraydo Base64 i do zapisu szesnastkowego oraz dostęp do surowego tekstu wJSON.parse. Temporal według listy TC39 trafi dopiero do ES2027.
Co roku komitet TC39 dopisuje do JavaScriptu kilka nowych funkcji, a w czerwcu Ecma International zatwierdza je jako kolejne wydanie standardu. W 2026 roku jest to ES2026. W tym artykule przejdziemy po wszystkich siedmiu nowościach, a każdą pokażemy na krótkim przykładzie. Cały kod uruchomiłem w przeglądarce Chromium 151, a przy każdej funkcji sprawdziłem też, co zwraca „stary” sposób, żebyś widział różnicę.
Czym jest ES2026 i kiedy został wydany?
ECMAScript to oficjalna specyfikacja, na podstawie której działa JavaScript. Przeglądarki i środowiska takie jak Node.js wdrażają ją na własnym tempie. Od wersji ES2015 nowe wydanie pojawia się co roku. ES2026 to 17. edycja standardu ECMA-262, zatwierdzona 30 czerwca 2026 roku na Zgromadzeniu Ogólnym Ecma.
Do wydania trafiają propozycje, które w TC39 osiągnęły ostatni, czwarty etap (stage 4).
Jakie nowości przynosi ES2026?
| Nowość | Do czego służy |
|---|---|
Array.fromAsync | tworzy tablicę ze źródła asynchronicznego (np. generatora async) |
Error.isError | pewnie sprawdza, czy wartość jest błędem |
Math.sumPrecise | sumuje liczby bez kumulowania błędów zaokrągleń |
Map.getOrInsert, getOrInsertComputed | pobiera wartość z mapy albo dodaje domyślną (także WeakMap) |
Iterator.concat | łączy kilka iteratorów w jeden |
Uint8Array.toBase64, fromBase64, toHex, fromHex | zamiana danych binarnych na Base64 lub zapis szesnastkowy i z powrotem |
JSON.parse z dostępem do źródła, JSON.rawJSON | zachowuje dokładny zapis liczb przy odczycie i zapisie JSON |
Jak zamienić źródło asynchroniczne na tablicę (Array.fromAsync)?
Array.from zna tylko zwykłe, synchroniczne kolekcje. Generator asynchroniczny (async function*) potraktuje jak pusty obiekt i zwróci pustą tablicę. Array.fromAsync czeka na kolejne wartości i zwraca obietnicę z gotową tablicą:
async function* liczby() {
yield 1;
yield 2;
yield 3;
}
console.log(Array.from(liczby())); // []
console.log(await Array.fromAsync(liczby())); // [1, 2, 3]
// z funkcją mapującą, jak w Array.from
console.log(await Array.fromAsync(liczby(), x => x * 10)); // [10, 20, 30]
// działa też dla tablicy obietnic
const obietnice = [
Promise.resolve("a"),
new Promise(resolve => setTimeout(() => resolve("b"), 10)),
];
console.log(await Array.fromAsync(obietnice)); // ["a", "b"]
Przydaje się, gdy czytasz dane strumieniowo (np. z pliku albo z API stronicowanego) i na końcu potrzebujesz zwykłej tablicy do dalszej obróbki metodami tablicowymi.
Jak sprawdzić, czy wartość jest błędem (Error.isError)?
Pytanie „czy to jest błąd?” wydaje się proste, ale instanceof Error zawodzi w dwóch sytuacjach: gdy błąd pochodzi z innego okna lub iframe (każde ma własny Error) oraz gdy ktoś podszyje się pod prototyp. Error.isError sprawdza wewnętrzną cechę obiektu, a nie łańcuch prototypów:
const blad = new TypeError("coś poszło nie tak");
console.log(Error.isError(blad)); // true
console.log(Error.isError({ name: "Error", message: "x" })); // false: zwykły obiekt
console.log(Error.isError("tekst")); // false
// błąd z innego okna: instanceof zawodzi, isError działa
const iframe = document.createElement("iframe");
document.body.append(iframe);
const obcy = new iframe.contentWindow.Error("z innego okna");
console.log(obcy instanceof Error); // false
console.log(Error.isError(obcy)); // true
// obiekt tylko z prototypem Error: instanceof się myli
const falszywy = Object.create(Error.prototype);
console.log(falszywy instanceof Error); // true
console.log(Error.isError(falszywy)); // false
Zwykle użyjesz tego w bloku catch, bo w JavaScripcie rzucić można dowolną wartość (throw "tekst"), więc nigdy nie masz pewności, co dostajesz.
Jak dokładnie zsumować liczby zmiennoprzecinkowe (Math.sumPrecise)?
Liczby w JavaScripcie zapisywane są binarnie, więc dodawanie ułamków kumuluje drobne błędy. Przy bardzo różnych rzędach wielkości błąd potrafi zjeść cały wynik. Math.sumPrecise liczy sumę dokładnie i dopiero na końcu zaokrągla:
const dane = [1e20, 0.1, -1e20];
console.log(dane.reduce((a, b) => a + b, 0)); // 0 – wynik zgubiony
console.log(Math.sumPrecise(dane)); // 0.1
const ceny = [0.1, 0.2, 0.3];
console.log(ceny.reduce((a, b) => a + b, 0)); // 0.6000000000000001
console.log(Math.sumPrecise(ceny)); // 0.6
Funkcja przyjmuje dowolny obiekt iterowalny, ale tylko liczby. Tekst w danych kończy się błędem, więc nie dostaniesz przypadkowej konkatenacji jak przy reduce:
try {
Math.sumPrecise([1, "2"]);
} catch (e) {
console.log(e.name); // TypeError
}
Przy pieniądzach nadal bezpieczniej liczyć w groszach (liczbach całkowitych). Math.sumPrecise jest dla danych, w których ułamki są nieuniknione, np. pomiarów.
Jak pobrać wartość z mapy albo dodać domyślną (Map.getOrInsert)?
Wzorzec „jeśli klucza nie ma, dodaj go” piszesz w JavaScripcie od lat: if (!mapa.has(klucz)) mapa.set(klucz, ...). Nowe metody robią to w jednym wywołaniu:
const grupy = new Map();
for (const slowo of ["ala", "ola", "adam", "oskar"]) {
grupy.getOrInsert(slowo[0], []).push(slowo); // grupowanie po pierwszej literze
}
console.log(grupy);
// Map { "a" => ["ala", "adam"], "o" => ["ola", "oskar"] }
getOrInsert zawsze tworzy wartość domyślną (tu pustą tablicę), nawet jeśli klucz już istnieje. Gdy jej utworzenie jest kosztowne, użyj wersji z funkcją, która zostanie wywołana tylko przy braku klucza (funkcja strzałkowa się tu sprawdza):
let wywolania = 0;
const cache = new Map();
const drogaFunkcja = klucz => {
wywolania++;
return klucz * 2;
};
cache.getOrInsertComputed(2, drogaFunkcja);
cache.getOrInsertComputed(2, drogaFunkcja);
console.log(cache, wywolania); // Map { 2 => 4 } 1
Obie metody działają też w WeakMap. Decyduje obecność klucza, a nie wartości: jeśli klucz istnieje i ma wartość undefined, dostaniesz właśnie undefined, a wartość domyślna zostanie zignorowana. Podobny efekt dla zwykłych obiektów daje operator ??=.
Jak połączyć kilka iteratorów w jeden (Iterator.concat)?
Iterator.concat przyjmuje dowolne obiekty iterowalne (tablice, zbiory, generatory) i zwraca jeden iterator, który przechodzi je po kolei. Zwrócony iterator ma metody z ES2025 (map, take, toArray):
const a = [1, 2];
const b = new Set([3, 4]);
function* c() {
yield 5;
}
console.log([...Iterator.concat(a, b, c())]); // [1, 2, 3, 4, 5]
console.log(
Iterator.concat(a, b, c())
.map(x => x * 2)
.take(3)
.toArray()
); // [2, 4, 6]
Dane są pobierane leniwie. take(3) zatrzymuje się po trzech elementach, więc reszta nie jest nawet odczytywana.
Jak zamienić dane binarne na Base64 lub zapis szesnastkowy?
Do tej pory btoa obsługiwało tylko tekst w jednym bajcie na znak, więc polskie litery kończyły się błędem. Teraz Uint8Array ma własne metody:
const bajty = new TextEncoder().encode("Cześć");
console.log(bajty.toBase64()); // "Q3plxZvEhw=="
console.log(bajty.toHex()); // "437a65c59bc487"
// wariant bezpieczny w adresach URL, bez wypełnienia
console.log(bajty.toBase64({ alphabet: "base64url", omitPadding: true })); // "Q3plxZvEhw"
// odwrotnie
const z = Uint8Array.fromBase64("Q3plxZvEhw==");
console.log(new TextDecoder().decode(z)); // "Cześć"
console.log(Uint8Array.fromHex("4142")); // Uint8Array [65, 66]
try {
btoa("Cześć");
} catch (e) {
console.log(e.name); // InvalidCharacterError
}
try {
Uint8Array.fromBase64("%%%");
} catch (e) {
console.log(e.name); // SyntaxError
}
Pierwsze wywołanie btoa z polskim tekstem rzuca błąd, a nowe metody poprawnie obsługują dowolne bajty, bo działają na Uint8Array, a nie na tekście.
Jak odczytać liczby z JSON bez utraty cyfr (JSON.parse ze źródłem i JSON.rawJSON)?
Zwykły JSON.parse zamienia liczby na typ number, który ma ograniczoną dokładność. Dłuższy identyfikator traci końcowe cyfry. Od ES2026 funkcja przekształcająca (reviver) dostaje trzeci argument z oryginalnym tekstem wartości:
const json = '{"id":12345678901234567890,"cena":9.90}';
console.log(JSON.parse(json).id); // 12345678901234567000 – cyfry zgubione
const dane = JSON.parse(json, (klucz, wartosc, kontekst) =>
klucz === "id" ? BigInt(kontekst.source) : wartosc
);
console.log(dane.id); // 12345678901234567890n
console.log(typeof dane.id); // "bigint"
console.log(dane.cena); // 9.9
Tekst źródłowy dostajesz tylko dla wartości prostych (liczba, tekst, true, false, null). Dla obiektów i tablic obiekt kontekst jest pusty. Drugą połowę problemu rozwiązuje JSON.rawJSON, bo JSON.stringify nie potrafi zapisać BigInt:
const id = 12345678901234567890n;
try {
JSON.stringify({ id });
} catch (e) {
console.log(e.name, e.message); // TypeError Do not know how to serialize a BigInt
}
console.log(JSON.stringify({ id: JSON.rawJSON(id.toString()) })); // {"id":12345678901234567890}
console.log(JSON.stringify({ x: JSON.rawJSON("1.50") })); // {"x":1.50}
console.log(JSON.isRawJSON(JSON.rawJSON("1"))); // true
rawJSON wstawia podany zapis do wyniku bez zmian, więc 1.50 zostaje 1.50, a nie 1.5.
Czy mogę używać nowości z ES2026 już dziś?
Zależy od środowiska. Sprawdziłem dwa:
- Chromium 151 ma wszystkie funkcje z tego artykułu.
- Node.js 22.22.3 ma tylko
Array.fromAsyncoraz odczyt źródła wJSON.parserazem zJSON.rawJSON. Pozostałe funkcje są tam niedostępne.
Firefoksa, Safari i nowszych wersji Node.js nie sprawdzałem, więc zajrzyj do aktualnych tabel zgodności (np. MDN), zanim użyjesz którejś w projekcie. Dostępność nowej funkcji możesz sprawdzić w samym kodzie i w razie braku użyć starszego zapisu:
if (typeof Array.fromAsync === "function") {
console.log("Array.fromAsync jest dostępne");
} else {
console.log("użyj pętli for await i push");
}
Czego nie ma w ES2026, a co zapowiada ES2027?
Według listy zakończonych propozycji TC39 do ES2027 trafi m.in. Temporal, czyli nowy interfejs do dat i czasu, oraz zarządzanie zasobami (using). Temporal osiągnął najwyższy etap w marcu 2026 roku, ale część serwisów pisze o nim jako o nowości ES2026. Wydanie zatwierdza Ecma, dlatego za podstawę bierz oficjalną specyfikację, a nie komentarze.
Jeśli chcesz uporządkować cały nowoczesny JavaScript pod okiem mentora, zobacz nasz kurs Zaawansowany JS.


