JavaScript, którego nie znasz — ukryte funkcje nowoczesnego ECMAScript

Odkryj mniej oczywiste funkcje nowoczesnego JavaScript. Object.groupBy, Promise.withResolvers, immutable array methods, Temporal, iterator helpers i więcej.

Opublikowano

18 września 2025 11:15

Czytanie

7 min czytania

Aktualizacja

15 kwietnia 2026 11:52

Specyfikacja ECMAScript ewoluuje co roku, ale większość developerów dowiaduje się o nowych funkcjach przypadkiem albo z tweetów wyrwanych z kontekstu. A między "fajnie wygląda w poście" a "nadaje się do produkcji" jest jeszcze kwestia wsparcia środowiska, bundlera i realnego use case'u.

Tymczasem w ostatnich rocznikach standardu pojawiło się sporo funkcji, które rozwiązują realne, codzienne problemy. Część jest gotowa do użycia od razu, część wymaga jeszcze ostrożności albo polyfillu. Ten artykuł pokazuje jedno i drugie.

Krótka odpowiedź: Najbardziej praktyczne nowości to Object.groupBy(), toSorted()/toReversed(), Set methods i Promise.withResolvers(), ale każdą z nich warto sprawdzić pod kątem wsparcia w docelowym runtime. Temporal nadal zwykle wymaga polyfillu, a iterator helpers są świeże i bardziej zależne od środowiska niż od samej daty publikacji specyfikacji.

Oto przegląd najciekawszych nowości, które prawdopodobnie przegapiłeś. Jeśli potrzebujesz odświeżenia fundamentów, wróć najpierw do takich tematów jak scope, closures, this, prototypy i Event Loop.

1. Object.groupBy() — koniec z reduce do grupowania

Ile razy pisałeś reduce() żeby pogrupować tablicę? Nigdy więcej:

Code
const products = [
  { name: 'iPhone', category: 'electronics', price: 999 },
  { name: 'MacBook', category: 'electronics', price: 1999 },
  { name: 'Desk', category: 'furniture', price: 299 },
  { name: 'Chair', category: 'furniture', price: 199 },
]
 
// ❌ Stary sposób — reduce hell
const grouped = products.reduce((acc, product) => {
  const key = product.category
  if (!acc[key]) acc[key] = []
  acc[key].push(product)
  return acc
}, {})
 
// ✅ ES2024 — jedna linia
const grouped = Object.groupBy(products, p => p.category)
// {
//   electronics: [{ name: 'iPhone', ... }, { name: 'MacBook', ... }],
//   furniture: [{ name: 'Desk', ... }, { name: 'Chair', ... }]
// }

Ważny detal: Object.groupBy() zwraca obiekt z null prototype. To zwykle zaleta, ale oznacza też, że metody w stylu grouped.hasOwnProperty(...) nie zadziałają tak jak w zwykłym obiekcie.

Jest też Map.groupBy() jeśli potrzebujesz Map zamiast obiektu:

Code
const groupedMap = Map.groupBy(products, p => p.category)
groupedMap.get('electronics') // [{ name: 'iPhone', ... }, ...]

To jedna z tych funkcji, które mają świetny stosunek zysku do nowości. Jeśli docelowe środowisko ją wspiera, bardzo szybko zastępuje ręczne reduce().

2. Promise.withResolvers() — externalizacja resolve/reject

Klasyczny problem: potrzebujesz resolve i reject poza konstruktorem Promise.

Code
// ❌ Stary sposób — brzydki workaround
let resolve, reject
const promise = new Promise((res, rej) => {
  resolve = res
  reject = rej
})
 
// Gdzieś indziej w kodzie
resolve('done')
 
// ✅ ES2024 — czysto i elegancko
const { promise, resolve, reject } = Promise.withResolvers()
 
// Gdzieś indziej w kodzie
resolve('done')

Przydatne w event-driven code (zobacz, jak działa pętla zdarzeń w JavaScript):

Code
function waitForEvent(element, eventName) {
  const { promise, resolve } = Promise.withResolvers()
  element.addEventListener(eventName, resolve, { once: true })
  return promise
}
 
// Użycie
await waitForEvent(button, 'click')
console.log('Button clicked!')

To wygodne, ale używaj świadomie. Promise.withResolvers() bardzo ułatwia tworzenie Promise "wiszących" w nieskończoność albo zostawianie listenerów bez cleanupu, jeśli zapomnisz o ścieżce błędu i anulowaniu.

3. Array.toSorted(), toReversed(), toSpliced() — immutable operations (ES2023, często pomijane)

Stare metody sort(), reverse(), splice() mutują oryginalną tablicę. To problem w React i innych miejscach gdzie immutability ma znaczenie. Więcej takich trików znajdziesz w 10 sztuczek w JavaScript.

Code
const numbers = [3, 1, 4, 1, 5]
 
// ❌ Mutacja oryginału
numbers.sort()  // numbers jest teraz [1, 1, 3, 4, 5]
 
// ✅ Nowa tablica, oryginał nietkniętny
const sorted = numbers.toSorted()  // [1, 1, 3, 4, 5]
console.log(numbers)  // [3, 1, 4, 1, 5] — bez zmian!
 
// toReversed
const reversed = numbers.toReversed()  // [5, 1, 4, 1, 3]
 
// toSpliced (immutable splice)
const modified = numbers.toSpliced(2, 1, 99)  // [3, 1, 99, 1, 5]

Plus with() — immutable update pojedynczego elementu:

Code
const arr = ['a', 'b', 'c']
const updated = arr.with(1, 'x')  // ['a', 'x', 'c']

W praktyce to jedne z najbardziej przydatnych nowości ostatnich lat, szczególnie w React, reducerach i wszędzie tam, gdzie nie chcesz przypadkiem mutować danych wejściowych.

4. Set methods — union, intersection, difference

Wreszcie! Operacje na zbiorach bez ręcznego iterowania:

Code
const a = new Set([1, 2, 3, 4])
const b = new Set([3, 4, 5, 6])
 
// Union — suma zbiorów
a.union(b)  // Set {1, 2, 3, 4, 5, 6}
 
// Intersection — część wspólna
a.intersection(b)  // Set {3, 4}
 
// Difference — elementy z A, których nie ma w B
a.difference(b)  // Set {1, 2}
 
// Symmetric difference — elementy unikalne dla każdego zbioru
a.symmetricDifference(b)  // Set {1, 2, 5, 6}
 
// Sprawdzanie relacji
a.isSubsetOf(new Set([1, 2, 3, 4, 5]))  // true
a.isSupersetOf(new Set([1, 2]))  // true
a.isDisjointFrom(new Set([7, 8, 9]))  // true

Jeśli pracujesz dużo na filtrach, uprawnieniach, tagach albo zbiorach identyfikatorów, te metody bardzo szybko zastępują własne helpery.

5. Iterator helpers — lazy evaluation

Nowe metody na iteratorach pozwalają na leniwe przetwarzanie danych:

Code
function* infiniteNumbers() {
  let i = 0
  while (true) {
    yield i++
  }
}
 
// Weź tylko 5 pierwszych parzystych
const result = infiniteNumbers()
  .filter(n => n % 2 === 0)
  .take(5)
  .toArray()
 
console.log(result)  // [0, 2, 4, 6, 8]

Ważny niuans: iterator helpers działają na iteratorach, nie bezpośrednio na tablicach. Dla tablic potrzebujesz np. Iterator.from(array) albo array.values():

Code
const result = Iterator.from([1, 2, 3, 4])
  .map((n) => n * 2)
  .toArray()

Dostępne metody:

  • map(fn) — transformacja
  • filter(fn) — filtrowanie
  • take(n) — pierwsze n elementów
  • drop(n) — pomiń pierwsze n
  • flatMap(fn) — map + flatten
  • reduce(fn, init) — agregacja
  • toArray() — konwersja do tablicy
  • forEach(fn) — iteracja z efektem ubocznym
  • some(fn) / every(fn) — predykaty
  • find(fn) — pierwszy pasujący

Różnica vs array methods: lazy evaluation. Iterator nie przetwarza wszystkiego od razu, tylko tyle, ile potrzeba. To robi różnicę przy dużych strumieniach danych, generatorach i pipeline'ach, których nie chcesz od razu materializować do tablicy.

6. Temporal API (ES2025) — koniec z Date i moment.js

Date w JavaScript jest... problematyczny. Temporal to kompletna wymiana:

Code
// Aktualna data i czas
const now = Temporal.Now.plainDateTimeISO()
// 2025-09-18T13:45:30.123456789
 
// Tworzenie dat
const date = Temporal.PlainDate.from('2025-12-25')
const time = Temporal.PlainTime.from('14:30:00')
const dateTime = Temporal.PlainDateTime.from('2025-12-25T14:30:00')
 
// Strefy czasowe — wreszcie działają poprawnie!
const warsaw = Temporal.ZonedDateTime.from('2025-12-25T14:30:00[Europe/Warsaw]')
const tokyo = warsaw.withTimeZone('Asia/Tokyo')

Operacje na datach:

Code
const date = Temporal.PlainDate.from('2025-01-15')
 
// Dodawanie/odejmowanie
date.add({ months: 2, days: 5 })  // 2025-03-20
date.subtract({ weeks: 1 })  // 2025-01-08
 
// Porównywanie
const other = Temporal.PlainDate.from('2025-06-01')
Temporal.PlainDate.compare(date, other)  // -1 (date < other)
date.until(other)  // P4M16D (4 miesiące, 16 dni)
 
// Formatowanie
date.toLocaleString('pl-PL', { dateStyle: 'full' })
// "środa, 15 stycznia 2025"

To najważniejsza "jakościowa" zmiana na tej liście, ale jednocześnie taka, do której najczęściej nadal potrzebujesz polyfillu. Jeśli dziś wdrażasz Temporal, najrozsądniej robić to przez @js-temporal/polyfill, a nie zakładać pełne natywne wsparcie wszędzie.

7. RegExp v flag — Unicode sets

Nowa flaga v rozszerza możliwości regex dla Unicode:

Code
// Operacje na zbiorach znaków
const emoji = /[\p{Emoji}--\p{ASCII}]/v
emoji.test('😀')  // true
emoji.test('A')   // false
 
// Intersection (&&)
const greekLetters = /[\p{Script=Greek}&&\p{Letter}]/v
 
// Subtraction (--)
const nonAsciiEmoji = /[\p{Emoji}--\p{ASCII}]/v
 
// Nested character classes
const complex = /[[a-z]&&[^aeiou]]/v  // spółgłoski

To funkcja niszowa, ale bardzo cenna tam, gdzie zwykłe regexy robią się nieczytelne: walidacja Unicode, parsery i operacje na klasach znaków.

8. Resizable ArrayBuffer

Bufory, które mogą zmieniać rozmiar bez kopiowania:

Code
// Stary sposób — trzeba tworzyć nowy bufor
const oldBuffer = new ArrayBuffer(10)
const newBuffer = new ArrayBuffer(20)
new Uint8Array(newBuffer).set(new Uint8Array(oldBuffer))
 
// ✅ ES2024 — resize w miejscu
const buffer = new ArrayBuffer(10, { maxByteLength: 100 })
buffer.resize(20)  // Rozszerz bez kopiowania
buffer.resize(5)   // Zmniejsz

Przydatne dla WebAssembly, binary protocols, streaming data.

To nie jest nowość dla każdego frontendowca, ale jeśli pracujesz z danymi binarnymi, przeglądarkowymi codecami albo WebAssembly, zaczyna być naprawdę użyteczna.

9. JSON modules (Import Assertions → Attributes)

Import JSON jako moduł ES:

Code
// Stara składnia (deprecated)
import data from './config.json' assert { type: 'json' }
 
// ✅ Nowa składnia (ES2025)
import data from './config.json' with { type: 'json' }
 
console.log(data.apiUrl)

Działa też z dynamic import:

Code
const config = await import('./config.json', { with: { type: 'json' } })

Tu warto uważać szczególnie na tooling. Sam standard to jedno, ale bundler, runtime i konfiguracja TypeScriptu mogą mieć własne wymagania albo inne domyślne zachowanie.

10. Float16Array — half-precision floats

Nowy typ tablicy dla 16-bitowych floatów — przydatne w machine learning:

Code
const f16 = new Float16Array([1.5, 2.5, 3.5])
 
// Konwersja
const f32 = new Float32Array(f16)  // do 32-bit
const f16Back = new Float16Array(f32)  // z powrotem
 
// Math.f16round — zaokrąglenie do half-precision
Math.f16round(1.337)  // 1.3369140625

Half-precision używa 2 bajty zamiast 4 (float32) lub 8 (float64). Oszczędność pamięci kosztem precyzji.

To funkcja mocno specjalistyczna. Jeśli nie pracujesz przy grafice, WebGPU, ML albo transmisji danych numerycznych, prawdopodobnie długo jej nie użyjesz. Ale dobrze wiedzieć, że język zaczyna pokrywać także takie scenariusze.

Jak sprawdzić wsparcie?

Code
// Feature detection
if (typeof Object.groupBy === 'function') {
  // Używaj Object.groupBy
} else {
  // Fallback
}
 
// Dla Temporal (polyfill)
import { Temporal } from '@js-temporal/polyfill'

Albo sprawdź na caniuse.com lub node.green. Przy nowszych funkcjach standardu to ważniejsze niż sama data publikacji specyfikacji.

FAQ

Co to jest Object.groupBy() i jak go używać?

Object.groupBy(array, keyFn) grupuje elementy tablicy według klucza zwracanego przez callback. Zastępuje ręczne reduce() do grupowania: Object.groupBy(products, p => p.category) zwraca obiekt gdzie klucze to wartości kategorii, a wartości to tablice pasujących elementów. Funkcja jest już dostępna w nowoczesnych przeglądarkach i nowych wersjach Node.js, ale przed wdrożeniem nadal sprawdź compatibility table dla swojego środowiska. Zwraca obiekt z null prototype — hasOwnProperty() na nim nie zadziała. Map.groupBy() to wersja zwracająca Map.

Czym są immutable array methods (toSorted, toReversed)?

ES2023 wprowadził niemutujące odpowiedniki starych metod tablicowych: toSorted() (zamiast sort()), toReversed() (zamiast reverse()), toSpliced() (zamiast splice()), with(index, value) (zamiast bezpośredniego przypisania). Stare metody mutują oryginalną tablicę — problem w React i wszędzie gdzie immutability ma znaczenie. Nowe zwracają nową tablicę, oryginał zostaje niezmieniony. To jedna z nowości z najszerszym i najbardziej praktycznym wsparciem, ale starsze środowiska nadal wymagają sprawdzenia compatibility table.

Co to jest Promise.withResolvers()?

Promise.withResolvers() zwraca obiekt { promise, resolve, reject } — pozwala "externalizować" resolve/reject poza konstruktor Promise. Przed ES2024 wymagało to brzydkiego workaroundu z let resolve; new Promise(res => { resolve = res }). Przydatne przy event-driven code, kolejkach i mostkowaniu callbackowego API, czyli Application Programming Interface, definiuje sposób komunikacji między aplikacjami lub modułami. do Promise. Uważaj na "wiszące" Promise — jeśli zapomnisz wywołać resolve lub reject, Promise nigdy się nie rozwiąże.

Co to jest Temporal API i kiedy go używać?

Temporal to nowy standard JavaScript zastępujący problematyczny obiekt Date. Oferuje czytelne typy: Temporal.PlainDate, Temporal.PlainTime, Temporal.ZonedDateTime z poprawną obsługą stref czasowych. Operacje: date.add({ months: 2 }), date.until(other), porównywanie. W ES2025, ale wsparcie przeglądarek jest jeszcze niepełne. Do produkcji używaj @js-temporal/polyfill. Długoterminowo zastąpi moment.js, date-fns i new Date().

Jak działają nowe Set methods w JavaScript?

ES2025 dodało metody operacji na zbiorach: set.union(other) (suma), set.intersection(other) (część wspólna), set.difference(other) (elementy w A, których nie ma w B), set.symmetricDifference(other) (unikalne dla każdego zbioru). Plus sprawdzanie relacji: isSubsetOf(), isSupersetOf(), isDisjointFrom(). Eliminują konieczność ręcznego iterowania przez dwa zbiory. Przydatne przy filtrach, uprawnieniach i tagach. W najnowszych silnikach są już dostępne, ale w starszych środowiskach wciąż mogą wymagać fallbacku.

Co to są Iterator helpers i czym różnią się od metod tablicowych?

Iterator helpers (filter, map, take, drop, flatMap, reduce, toArray) działają na iteratorach, nie na tablicach. Kluczowa różnica: lazy evaluation — operacje są wykonywane tylko dla tylu elementów, ile potrzebujesz, a nie dla całej kolekcji naraz. Idealne dla generatorów, nieskończonych sekwencji i dużych zbiorów danych. Dla tablic potrzebujesz Iterator.from(array). To jedna z bardziej świeżych nowości, więc przed wdrożeniem do produkcji szczególnie warto sprawdzić wsparcie w runtime i testach CI.

Jak sprawdzić czy nowa funkcja JavaScript jest gotowa do użycia?

Feature detection w kodzie: if (typeof Object.groupBy === 'function') { ... }. Zewnętrzne narzędzia: caniuse.com (wsparcie przeglądarek), node.green (wsparcie Node.js), MDN Web Docs (dokumentacja + compatibility table). Przy budowaniu na polyfillach: sprawdź czy Babel/TypeScript poprawnie transpiluje. Ważna zasada: data publikacji specyfikacji ≠ dostępność w przeglądarkach — może minąć rok lub więcej.

Źródła i dokumentacja

Podsumowanie

ES2024/2025 przynosi funkcje, które eliminują potrzebę wielu utility libraries:

ProblemStare rozwiązanieES2024/2025
Grupowanielodash.groupByObject.groupBy
Operacje na zbiorachręczne iterowanieSet methods
Immutable array ops[...arr].sort()toSorted(), toReversed()
Daty i strefy czasowemoment.js, date-fnsTemporal API
Promise externalizationworkaroundyPromise.withResolvers
Lazy iterationwłasna implementacjaIterator helpers

JavaScript staje się coraz bardziej kompletnym językiem, ale nie każda nowość od razu nadaje się do produkcji w każdym projekcie. Najlepsze podejście to traktować te funkcje jak nowe narzędzia w skrzynce: sprawdzić wsparcie, zrozumieć trade-offy i wdrażać tam, gdzie faktycznie upraszczają kod. A jeśli chcesz optymalnie korzystać z Promise, przeczytaj też o pułapkach async/await i mocy Promise.all.


Chcesz pisać czystszy kod? Sprawdź 10 sztuczek w JavaScript albo dołóż do tego solidne zrozumienie scope, closures i asynchroniczności.

Pracuję z tym zawodowo.

Jeśli chcesz przełożyć ten temat na lepszą architekturę frontendu, uporządkować React lub Next.js i podnieść jakość pracy zespołu, skontaktuj się ze mną. Pomagam zamieniać wiedzę z artykułów w praktyczne decyzje technologiczne.

O autorze

Maciej Sala

Maciej Sala — project manager i frontendowiec z doświadczeniem w marketingu internetowym. Na co dzień pracuję z Reactem, Next.js i TypeScriptem, łącząc perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat związany z branżą gier wideo jako project manager i game designer.

Absolwent historii na Uniwersytecie Jagiellońskim i studiów podyplomowych z marketingu internetowego na Akademii Górniczo-Hutniczej w Krakowie. Poza pracą trenuje na siłowni, maluje figurki i realizuje własne projekty.

Biblioteka wiedzy

Czytaj dalej

Zobacz więcej wpisów
Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?

Astro.js vs Next.js — które narzędzie wybrać w 2026 roku?

Fachowe porównanie Astro.js i Next.js z perspektywy developera pracującego na co dzień w Next.js. Architektura, wydajność, SEO, DX, koszty i konkretne use case — z benchmarkami i przykładami kodu.

Maciej Sala

Maciej Sala

Founder Strivelab