JavaScript у 2026 році: повний огляд ES2026 та майбутніх можливостей

Станом на 5 серпня 2026 року ECMAScript 2026 — це вже не набір прогнозів, а офіційно опублікований стандарт. Він вийшов у червні як 17-та редакція ECMA-262. Окремо опублікована 13-та редакція ECMA-402 — стандарту міжнародних API Intl. ECMA-262:2026, ECMA-402:2026.

До основного ES2026 увійшли вісім груп змін: точне підсумовування чисел, з’єднання ітераторів, створення масивів з асинхронних джерел, перевірка об’єктів помилок, нові методи Map і WeakMap, перетворення Uint8Array у Base64 і Hex, доступ до вихідного тексту під час розбору JSON та вставлення «сирих» значень у JSON.

У цій статті під JavaScript маються на увазі стандарти ECMA-262 і ECMA-402. DOM, fetch, Web Crypto, Web Streams, API Node.js, TypeScript і можливості окремих фреймворків сюди не входять.

Як читати статуси пропозицій

  • ES2026 — можливість уже є частиною стандарту, хоча конкретний браузер або серверне середовище може ще її не реалізувати.

  • Stage 4 — дизайн завершено; можливість затверджена для майбутньої редакції стандарту.

  • Stage 3 — специфікація практично готова, але потрібні реалізації та досвід використання.

  • Stage 2.7 — текст специфікації завершено, проте пропозиція ще проходить перевірку перед реалізаціями.

  • Stage 2 — TC39 очікує подальшого розвитку можливості, але її API та синтаксис можуть змінитися.

Стандартизація і підтримка — не одне й те саме. Метод може вже входити до ES2026, але бути відсутнім у старій версії браузера. І навпаки: рушій може експериментально підтримувати пропозицію, яка ще не стала стандартом.

Усі нововведення ECMAScript 2026

1. Math.sumPrecise() — точніше підсумовування чисел

Звичайне додавання чисел із рухомою комою накопичує похибку. Особливо добре це видно, коли в одному наборі є дуже великі та дуже малі значення:

const values = [1e20, 0.1, -1e20];

values.reduce((sum, value) => sum + value, 0);
// 0

Math.sumPrecise(values);
// 0.1

Math.sumPrecise(iterable) приймає будь-який ітерабельний об’єкт із числами та виконує підсумовування зі значно кращим контролем похибки.

Особливості:

  • приймає саме значення типу Number;

  • не змішує Number і BigInt;

  • для порожнього набору повертає -0;

  • працює з масивами, множинами, генераторами та іншими ітерабельними джерелами.

Метод особливо корисний для статистики, фінансових розрахунків із Number, графіки та обробки великих числових наборів. Для точних десяткових грошей він усе одно не замінює десяткові типи або спеціалізовані бібліотеки. Офіційна пропозиція Math.sumPrecise.

2. Iterator.concat() — ліниве з’єднання ітераторів

Статичний метод Iterator.concat(...iterables) послідовно об’єднує кілька ітерабельних джерел:

const result = Iterator
  .concat([1, 2], new Set([3, 4]), [5])
  .toArray();

console.log(result);
// [1, 2, 3, 4, 5]

На відміну від array1.concat(array2), новий метод:

  • не обмежений масивами;

  • не копіює всі значення наперед;

  • отримує елементи лише тоді, коли споживач їх запитує;

  • може працювати з генераторами та потенційно нескінченними послідовностями.

function* naturalNumbers() {
  let value = 1;

  while (true) {
    yield value++;
  }
}

const firstFive = Iterator
  .concat([0], naturalNumbers())
  .take(5)
  .toArray();

// [0, 1, 2, 3, 4]

Методи take() і toArray() у цьому прикладі з’явилися ще в ES2025 як частина Iterator Helpers. Офіційна пропозиція Iterator Sequencing.

3. Array.fromAsync() — масив з асинхронного джерела

Array.fromAsync(source, mapFn?, thisArg?) є асинхронним аналогом Array.from(). Метод повертає Promise, результатом якого буде масив.

Він приймає:

  • асинхронні ітератори;

  • звичайні ітератори;

  • синхронні й асинхронні значення;

  • масивоподібні об’єкти;

  • необов’язкову асинхронну функцію перетворення.

async function* loadIds() {
  yield 1;
  yield 2;
  yield 3;
}

const users = await Array.fromAsync(
  loadIds(),
  async id => loadUser(id)
);

Кожне значення та результат mapFn очікуються через await. Оброблення відбувається послідовно зі збереженням порядку. Тому Array.fromAsync() не є паралельною заміною Promise.all():

// Послідовне очікування
const sequential = await Array.fromAsync(
  urls,
  url => fetch(url)
);

// Паралельний запуск
const parallel = await Promise.all(
  urls.map(url => fetch(url))
);

Як і Array.from(), метод є узагальненим: його можна викликати з іншим конструктором через call. Офіційна пропозиція Array.fromAsync.

4. Error.isError() — надійна перевірка помилок

До ES2026 помилки зазвичай перевіряли через instanceof:

value instanceof Error

Але така перевірка може повернути false для справжньої помилки з іншого контексту виконання — наприклад, з iframe або іншого realm.

Error.isError(new TypeError("Помилка"));
// true

Error.isError({
  name: "Error",
  message: "Я лише схожий на помилку"
});
// false

Error.isError(value) перевіряє внутрішній тип об’єкта. Його не можна обдурити лише властивостями name, message або Symbol.toStringTag. Офіційна пропозиція Error.isError.

5. Map і WeakMap: отримання або створення значення

ES2026 додає чотири методи:

Map.prototype.getOrInsert(key, defaultValue)
Map.prototype.getOrInsertComputed(key, callback)

WeakMap.prototype.getOrInsert(key, defaultValue)
WeakMap.prototype.getOrInsertComputed(key, callback)

Вони перевіряють саме наявність ключа, а не істинність значення. Якщо ключ існує, повертається його поточне значення. Якщо ні — значення додається до колекції та повертається.

const settings = new Map();

settings.getOrInsert("theme", "dark");
// "dark"

settings.getOrInsert("theme", "light");
// "dark"

Варіант getOrInsertComputed() обчислює значення лише тоді, коли ключа ще немає:

const groups = new Map();

for (const product of products) {
  groups
    .getOrInsertComputed(product.category, () => [])
    .push(product);
}

Функція отримує ключ як аргумент:

const cache = new Map();

const value = cache.getOrInsertComputed(
  userId,
  id => createUserCache(id)
);

Якщо створення значення дороге, треба використовувати getOrInsertComputed(). Аргумент для звичайного getOrInsert() обчислюється ще до виклику методу, навіть якщо ключ уже існує. Офіційна пропозиція Upsert.

6. Base64 і Hex безпосередньо в Uint8Array

ES2026 додає шість методів для перетворення байтів:

Uint8Array.fromBase64(string, options?)
Uint8Array.fromHex(string)

Uint8Array.prototype.toBase64(options?)
Uint8Array.prototype.toHex()

Uint8Array.prototype.setFromBase64(string, options?)
Uint8Array.prototype.setFromHex(string)

Приклад:

const bytes = new TextEncoder().encode("Привіт");

const encoded = bytes.toBase64({
  alphabet: "base64url",
  omitPadding: true
});

const restored = Uint8Array.fromBase64(encoded, {
  alphabet: "base64url"
});

Для Base64 доступні такі параметри:

  • alphabet: "base64" — звичайний алфавіт;

  • alphabet: "base64url" — URL-безпечний варіант;

  • omitPadding: true — не додавати завершальні символи =;

  • lastChunkHandling: "loose" — стандартний поблажливий режим декодування;

  • lastChunkHandling: "strict" — вимагати правильний останній блок;

  • lastChunkHandling: "stop-before-partial" — зупинитися перед неповним блоком.

Методи setFromBase64() і setFromHex() записують дані в уже створений масив та повертають кількість прочитаних і записаних одиниць:

const target = new Uint8Array(64);

const { read, written } = target.setFromBase64(encoded);

toHex() створює рядок у нижньому регістрі. fromHex() приймає як великі, так і малі літери. Base64-декодер може пропускати ASCII-пробіли. Офіційна пропозиція Base64/Hex для Uint8Array.

7. Вихідний текст у JSON.parse()

Функція-відновлювач JSON.parse() тепер може отримувати третій аргумент — об’єкт контексту:

JSON.parse(text, (key, value, context) => {
  // context.source
  return value;
});

Для примітивних JSON-значень context.source містить точний фрагмент початкового JSON. Це розв’язує проблему великих цілих чисел, які не можна точно подати типом Number:

const result = JSON.parse(
  '{"id": 9007199254740993123456789}',
  (key, value, context) => {
    if (key === "id") {
      return BigInt(context.source);
    }

    return value;
  }
);

console.log(result.id);
// 9007199254740993123456789n

Без context.source значення вже було б округлене під час перетворення на Number.

Контекст із початковим текстом надається для примітивів. Для масивів та об’єктів source не використовується, оскільки їхні дочірні значення могли бути змінені функцією-відновлювачем. Офіційна пропозиція JSON Parse Source.

8. JSON.rawJSON() і JSON.isRawJSON()

JSON.rawJSON(text) створює спеціальний незмінний об’єкт, вміст якого JSON.stringify() вставляє без повторного перетворення:

const json = JSON.stringify(
  { id: 9007199254740993123456789n },
  (key, value) => {
    return typeof value === "bigint"
      ? JSON.rawJSON(String(value))
      : value;
  }
);

console.log(json);
// {"id":9007199254740993123456789}

Перевірити такий об’єкт можна через:

const value = JSON.rawJSON("123");

JSON.isRawJSON(value);
// true

Переданий текст обов’язково перевіряється як коректний JSON-примітив. Це не механізм довільного вставлення фрагментів, об’єктів або масивів і не спосіб обійти синтаксичну перевірку JSON.

Найважливіші сценарії — точне серіалізування великих цілих чисел, десяткових значень та інших даних, для яких звичайного Number недостатньо. Можливість входить до тієї самої пропозиції JSON Parse Source.

Нововведення Intl у 2026 році

ECMA-402:2026 містить дві завершені пропозиції: Locale Info та правила для era, eraYear і monthCode. Офіційний перелік завершених пропозицій ECMA-402.

Intl.Locale: інформація про локаль

До Intl.Locale.prototype додано:

getCalendars()
getCollations()
getHourCycles()
getNumberingSystems()
getTimeZones()
getTextInfo()
getWeekInfo()

Також з’явилися властивості:

locale.firstDayOfWeek
locale.variants

Приклад:

const locale = new Intl.Locale("uk-UA");

const week = locale.getWeekInfo();
// { firstDay, weekend }

const text = locale.getTextInfo();
// { direction: "ltr" }

const calendars = locale.getCalendars();
const timeZones = locale.getTimeZones();

У getWeekInfo() дні нумеруються від 1 до 7: понеділок — 1, неділя — 7.

getTimeZones() повертає часові пояси для явно зазначеного регіону. Якщо тег локалі не має регіону, результатом може бути undefined:

new Intl.Locale("uk").getTimeZones();
// може бути undefined

new Intl.Locale("uk-UA").getTimeZones();
// масив часових поясів для регіону UA

firstDayOfWeek представляє явно вибране значення Unicode-розширення:

const locale = new Intl.Locale("en-US", {
  firstDayOfWeek: "mon"
});

locale.firstDayOfWeek;
// "mon"

variants повертає частину мовного тегу з варіантами або undefined, якщо варіантів немає. Конкретні списки календарів, колацій, систем числення та часових поясів можуть залежати від локалізаційних даних реалізації. Офіційна пропозиція Intl Locale Info.

era, eraYear і monthCode

Друга зміна ECMA-402 визначає точну поведінку календарних полів:

  • era;

  • eraYear;

  • monthCode.

Це важливо для японського, китайського, єврейського, буддійського та інших не-ISO календарів. Наприклад, звичайний номер місяця не завжди достатній для представлення вставного місяця, тому використовується стабільний monthCode.

Зміна переважно визначає календарну семантику для Temporal. Вона не створює окремого глобального конструктора Intl.Era. Публічно її ефект буде помітний у середовищах із реалізованим Temporal, зокрема через поля Temporal.PlainDate, Temporal.PlainDateTime, Temporal.PlainYearMonth і Temporal.ZonedDateTime. Офіційна пропозиція Intl Era and MonthCode.

Уже затверджено для майбутнього стандарту: Stage 4 та очікуваний ES2027

На серпень 2026 року чотири нові групи вже досягли Stage 4 та мають очікуваний рік публікації 2027: Explicit Resource Management, Atomics.pause, Joint Iteration і Temporal. Отже, їхній дизайн завершено, хоча універсальної підтримки в усіх середовищах ще немає. Офіційний перелік завершених пропозицій TC39.

Explicit Resource Management

Пропозиція вводить синтаксис автоматичного звільнення ресурсів:

{
  using file = openFile();
  file.write("дані");
} // file автоматично звільняється

Для асинхронних ресурсів:

await using connection = await openConnection();

Новий API:

Symbol.dispose
Symbol.asyncDispose

DisposableStack
AsyncDisposableStack
SuppressedError

Основні методи DisposableStack:

stack.use(resource)
stack.adopt(value, onDispose)
stack.defer(onDispose)
stack.move()
stack.dispose()
stack.disposed
stack[Symbol.dispose]()

Асинхронний варіант надає disposeAsync() та Symbol.asyncDispose. Ресурси закриваються у зворотному порядку. SuppressedError зберігає і основну помилку, і помилку, що виникла під час звільнення ресурсу. Офіційна пропозиція Explicit Resource Management.

Atomics.pause()

Atomics.pause();
Atomics.pause(iteration);

Метод надає процесору підказку під час короткого активного очікування у багатопотоковому коді. Він не замінює Atomics.wait() і не гарантує конкретної тривалості паузи — його семантичний ефект обмежений продуктивністю та плануванням виконання. Офіційна пропозиція Atomics.pause.

Iterator.zip() і Iterator.zipKeyed()

const rows = Iterator
  .zip([
    ["Анна", "Богдан"],
    [25, 31]
  ])
  .toArray();

// [["Анна", 25], ["Богдан", 31]]

Для іменованих джерел:

const rows = Iterator.zipKeyed({
  name: ["Анна", "Богдан"],
  age: [25, 31]
}).toArray();

// [{ name: "Анна", age: 25 }, ...]

Передбачено режими:

  • "shortest" — завершитися разом із найкоротшим джерелом;

  • "longest" — працювати до завершення найдовшого, використовуючи padding;

  • "strict" — вимагати однакової довжини.

Офіційна пропозиція Joint Iteration.

Temporal — нова система дат і часу

Temporal має замінити більшість складних сценаріїв із застарілим Date.

До глобального об’єкта входять:

Temporal.Now
Temporal.Instant
Temporal.ZonedDateTime
Temporal.PlainDate
Temporal.PlainTime
Temporal.PlainDateTime
Temporal.PlainYearMonth
Temporal.PlainMonthDay
Temporal.Duration

Приклад:

const meeting = Temporal.ZonedDateTime.from(
  "2026-10-25T09:00[Europe/Kyiv]"
);

const later = meeting.add({ hours: 3 });

console.log(later.toString());

Основні родини методів:

  • статичні from() і compare();

  • add() та subtract();

  • until() і since();

  • round();

  • with();

  • equals();

  • toString(), toJSON(), toLocaleString();

  • перетворення між датою, часом, часовим поясом та миттю.

Об’єкти Temporal незмінні. API підтримує часові пояси, переходи на літній час, не-ISO календарі та точність до наносекунд. Пропозиція вже затверджена, але міжплатформна підтримка залишається нерівномірною, тому перед використанням потрібна перевірка або polyfill. Офіційна документація Temporal.

Майбутні можливості Stage 3

Stage 3 означає, що дизайн достатньо стабільний для реалізації, але жодній із цих можливостей ще не гарантовано конкретний рік публікації. Актуальний перелік ведеться в реєстрі активних пропозицій TC39.

Пропозиція Майбутній API або зміна
Legacy RegExp Features Стандартизація фактично наявних застарілих властивостей на зразок RegExp.$1 для вебсумісності. Для нового коду вони не рекомендуються
Source Phase Imports import source moduleSource from "./module.wasm" та import.source() для отримання джерела модуля без звичайного виконання
Dynamic Code Brand Checks Захисні перевірки для eval() і Function; переважно інтеграція з політиками безпеки середовища
Deferring Module Evaluation import defer * as module from "./heavy.js" — завантаження графа модулів із відкладеним виконанням
Non-extensible Applies to Private Object.preventExtensions() впливатиме також на подальше додавання приватних елементів класу
Import Text import text from "./template.txt" with { type: "text" }
Iterator Chunking Ліниві Iterator.prototype.chunks(size) і windows(size)
Iterator Includes Iterator.prototype.includes(value)
Iterator Join Iterator.prototype.join(separator) без проміжного масиву
Error Stack Accessor Стандартизація фактично поширеної властивості Error.prototype.stack
RegExp Buffer Boundaries \A — початок усього введення, \z — абсолютний кінець, \Z — кінець перед необов’язковим фінальним переносом рядка; потрібен прапорець u або v
Await Dictionary Promise.allKeyed(object) і Promise.allSettledKeyed(object), які зберігають ключі об’єкта
Intl: Keep Trailing Zeros Intl.NumberFormat і Intl.PluralRules зберігатимуть кінцеві нулі у числових рядках: "1.0" не перетворюватиметься автоматично на "1"

Приклад Promise.allKeyed():

const data = await Promise.allKeyed({
  user: loadUser(),
  settings: loadSettings(),
  messages: loadMessages()
});

console.log(data.user);

Операції запускаються паралельно, але результат має ті самі власні перелічувані рядкові та символьні ключі.

Пропозиції Stage 2.7

Станом на дату статті у зведеному списку TC39 перебувають шість таких пропозицій.

Decorators

@logged
class Service {
  @memoized
  calculate() {}
}

Декоратори зможуть змінювати або доповнювати класи, методи, поля, гетери та сетери. Підтримка декораторів у TypeScript або Babel не означає, що нативний JavaScript-рушій уже підтримує фінальний стандартний синтаксис. Офіційна пропозиція Decorators.

Decorator Metadata

Декоратори отримають context.metadata, а готовий клас — властивість із ключем Symbol.metadata:

function entity(name) {
  return (value, context) => {
    context.metadata.entityName = name;
  };
}

@entity("users")
class User {}

User[Symbol.metadata].entityName;
// "users"

Офіційна пропозиція Decorator Metadata.

ShadowRealm

const realm = new ShadowRealm();

const result = realm.evaluate("1 + 2");
const fn = await realm.importValue("./module.js", "exportedFunction");

ShadowRealm створює ізольоване глобальне середовище виконання. Значення між середовищами передаються з обмеженнями, щоб не руйнувати ізоляцію. Офіційна пропозиція ShadowRealm.

ESM Phase Imports

Ця пропозиція створює загальну модель фаз імпорту модулів, на якій будуються import source, відкладене виконання та інші спеціалізовані види імпорту. Її головна роль — узгодити синтаксис і внутрішню модель модульної системи. Офіційна пропозиція ESM Phase Imports.

Immutable ArrayBuffers

Запропоновано властивість і два методи:

buffer.immutable
buffer.transferToImmutable(newByteLength?)
buffer.sliceToImmutable(start?, end?)

Незмінний ArrayBuffer не можна модифікувати, від’єднати, змінити його розмір або повторно передати. Це дає змогу безпечніше обмінюватися великими двійковими даними та в окремих реалізаціях уникати копіювання. Офіційна пропозиція Immutable ArrayBuffers.

Import Bytes

import bytes from "./photo.png" with { type: "bytes" };

const { default: dynamicBytes } = await import(
  "./photo.png",
  { with: { type: "bytes" } }
);

Результатом має бути Uint8Array, побудований на незмінному ArrayBuffer. Це уніфікує імпорт зображень, шрифтів, моделей та інших двійкових файлів у браузерних і серверних середовищах. Офіційна пропозиція Import Bytes.

Що ще перебуває на Stage 2

Stage 2 — це не анонс майбутнього релізу. Такі пропозиції можуть суттєво змінити назви, синтаксис або взагалі не дійти до стандарту.

Актуальний основний список містить:

  • function.sent;

  • throw expressions;

  • приховування реалізації функцій;

  • нормалізацію ключів колекцій;

  • Module Expressions;

  • Pipeline Operator;

  • деструктуризацію приватних полів;

  • String.dedent;

  • JSON.parseImmutable;

  • Module Declarations;

  • Symbol Predicates;

  • Async Iterator Helpers;

  • Iterator.range;

  • Async Context;

  • discard-оголошення через void;

  • передавання активного ScriptOrModule через асинхронні задачі;

  • Structs;

  • Extractors;

  • Deferred Re-exports;

  • SeededPRNG;

  • Math.clamp;

  • перевірку справжніх нативних Promise;

  • Error.captureStackTrace;

  • Object.keysLength;

  • синхронний імпорт модулів;

  • обмеження можливостей thenable-об’єктів;

  • тип або представлення Amount;

  • стандартизовану властивість коду помилки;

  • fused multiply-add для точнішого обчислення a × b + c.

У ECMA-402 на Stage 2 перебувають параметр eraDisplay для Intl.DateTimeFormat та додаткові варіанти відображення валют. Повний актуальний склад завжди слід перевіряти в офіційному списку TC39 та списку пропозицій ECMA-402.

Stage 0 і Stage 1 тут свідомо не перелічені: це ранні дослідницькі ідеї, а не можливості, анонсовані для додавання до JavaScript.

Як перевіряти підтримку

Для нових функцій і методів найнадійнішим залишається feature detection:

const support = {
  sumPrecise:
    typeof Math.sumPrecise === "function",

  iteratorConcat:
    typeof globalThis.Iterator?.concat === "function",

  fromAsync:
    typeof Array.fromAsync === "function",

  isError:
    typeof Error.isError === "function",

  mapDefaults:
    typeof Map.prototype.getOrInsert === "function",

  base64:
    typeof Uint8Array.fromBase64 === "function",

  rawJSON:
    typeof JSON.rawJSON === "function",

  localeInfo:
    typeof globalThis.Intl?.Locale?.prototype?.getWeekInfo === "function",

  temporal:
    typeof globalThis.Temporal === "object"
};

Для нового синтаксису ситуація складніша. Непідтримувані using, декоратори або import defer спричинять синтаксичну помилку ще до запуску програми, тому їх не можна безпечно загорнути у звичайний if.

Для таких можливостей потрібні:

  • окремі модулі для різних цільових середовищ;

  • налаштований транспайлер;

  • контроль мінімальних версій середовища;

  • або очікування нативної підтримки.

Методи на зразок Error.isError(), Map.prototype.getOrInsert() чи Math.sumPrecise() часто можна доповнити polyfill-реалізаціями. Новий синтаксис і деякі внутрішні можливості рушія повноцінно поліфілити неможливо.

Підсумок

ES2026 — невелика, але дуже практична редакція JavaScript. Її головний напрям — усунення повсякденного шаблонного коду:

  • точніші числові обчислення через Math.sumPrecise();

  • зручніше асинхронне створення масивів;

  • ліниве об’єднання ітераторів;

  • безпечна перевірка помилок;

  • автоматичне створення значень у Map і WeakMap;

  • нативна робота Uint8Array з Base64 та Hex;

  • точніше читання й запис JSON;

  • доступ до культурних параметрів локалі через Intl.Locale.

Найбільші архітектурні зміни — Temporal, автоматичне керування ресурсами та спільне ітерування — уже пройшли Stage 4, але заплановані для ES2027. Пропозиції Stage 3 можна вважати серйозними кандидатами, проте ще не частиною стандартного JavaScript.

Тому у виробничому коді варто орієнтуватися не лише на номер ECMAScript, а й на фактичні можливості цільових браузерів і серверних середовищ.

Дата актуальності матеріалу: 5 серпня 2026 року.