JavaScript и Web APIs: ключевые изменения 2018–2022
Обзор
Период 2018–2022 характеризуется ежегодными ECMAScript релизами (ES2018–ES2022) и значительным расширением Web APIs. Основные тренды:
- ECMAScript: стабильные ежегодные релизы с incremental улучшениями
- Async/Performance APIs: Core Web Vitals measurement, Observers расширение
- Storage & State: IndexedDB улучшения, Cookie Store API начало
- Security: Permissions API расширение, Feature Policy / Permissions Policy
- Developer Experience: Optional chaining, nullish coalescing, top-level await
ECMAScript (Language Features)
ES2018
Async iteration:
for await (const item of asyncIterable) {
console.log(item);
}Rest/Spread properties для объектов:
const { x, y, ...rest } = { x: 1, y: 2, a: 3, b: 4 };
// rest = { a: 3, b: 4 }
const merged = { ...obj1, ...obj2 };Promise.finally():
fetch(url)
.then(handleResponse)
.catch(handleError)
.finally(() => hideLoader());ES2019
Array.flat() и Array.flatMap():
[1, [2, [3, 4]]].flat(2); // [1, 2, 3, 4]
[1, 2, 3].flatMap((x) => [x, x * 2]); // [1, 2, 2, 4, 3, 6]Object.fromEntries():
const entries = [
['a', 1],
['b', 2],
];
const obj = Object.fromEntries(entries); // { a: 1, b: 2 }String.trimStart() / trimEnd():
' hello '.trimStart(); // 'hello '
' hello '.trimEnd(); // ' hello'ES2020 — самый значимый релиз периода
Optional Chaining (?.):
const street = user?.address?.street;
// Вместо: user && user.address && user.address.streetNullish Coalescing (??):
const value = config.timeout ?? 3000;
// Отличие от || : 0 и '' считаются валиднымиBigInt — arbitrary precision integers:
const huge = 9007199254740991n + 1n;
typeof huge; // 'bigint'Promise.allSettled():
const results = await Promise.allSettled([
fetch('/api/user'),
fetch('/api/posts'),
fetch('/api/comments'),
]);
// Ждёт завершения всех промисов, игнорируя ошибкиglobalThis:
// Универсальный доступ к global объекту
globalThis.fetch; // Работает везде: браузер, Node.js, workersDynamic import():
const module = await import('./module.js');ES2021
Logical Assignment Operators:
x ||= y; // x || (x = y)
x &&= y; // x && (x = y)
x ??= y; // x ?? (x = y)Numeric Separators:
const billion = 1_000_000_000;
const hex = 0xff_ff_ff_ff;String.replaceAll():
'hello world'.replaceAll('l', 'L'); // 'heLLo worLd'Promise.any():
const first = await Promise.any([
fetch('/api/cdn1/data'),
fetch('/api/cdn2/data'),
fetch('/api/cdn3/data'),
]);
// Resolves с первым успешным промисомES2022
Top-level await:
// В модулях, без async function wrapper
const data = await fetch('/api/config').then((r) => r.json());
export const config = data;Array.at():
const arr = [1, 2, 3];
arr.at(-1); // 3 (последний элемент)
arr.at(-2); // 2 (предпоследний)Object.hasOwn():
Object.hasOwn(obj, 'prop'); // Лучше чем obj.hasOwnProperty('prop')Class Fields:
class Counter {
// Public fields
count = 0;
// Private fields
#secret = 'private';
// Static fields
static instances = 0;
constructor() {
Counter.instances++;
}
}Error.cause:
try {
await fetch('/api');
} catch (err) {
throw new Error('Failed to load data', { cause: err });
}Web APIs — Observers
Intersection Observer v2
Baseline: ~2019-2020
Новое: отслеживание actual visibility (не только intersection)
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
// Элемент в viewport
console.log('Видим элемент');
}
});
},
{
threshold: [0, 0.5, 1],
rootMargin: '50px',
},
);
observer.observe(element);Практическое применение:
- Lazy loading изображений
- Infinite scroll
- Отслеживание видимости для аналитики
- Анимации при появлении
Resize Observer
Baseline: Март 2020
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
console.log('Размер изменился:', entry.contentRect);
// Адаптивная логика
if (entry.contentRect.width < 400) {
entry.target.classList.add('compact');
}
}
});
observer.observe(element);Заменяет: window.resize listeners с лучшей производительностью
Performance APIs — Core Web Vitals
Paint Timing API
Baseline: ~2019
const paintEntries = performance.getEntriesByType('paint');
paintEntries.forEach((entry) => {
console.log(`${entry.name}: ${entry.startTime}ms`);
});
// first-paint: 123ms
// first-contentful-paint: 456msElement Timing API
Baseline: 2020
<img src="hero.jpg" elementtiming="hero-image" />const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('LCP candidate:', entry.renderTime);
}
});
observer.observe({ type: 'element', buffered: true });Layout Instability API (CLS)
Baseline: 2020
let cls = 0;
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
cls += entry.value;
}
}
});
observer.observe({ type: 'layout-shift', buffered: true });Async & Control Flow
AbortController / AbortSignal
Baseline: 2020-2021
const controller = new AbortController();
const signal = controller.signal;
// Fetch с отменой
fetch('/api/data', { signal })
.then(handleData)
.catch((err) => {
if (err.name === 'AbortError') {
console.log('Запрос отменён');
}
});
// Отмена через 5 секунд
setTimeout(() => controller.abort(), 5000);
// Также работает с addEventListener
element.addEventListener('click', handler, { signal });
controller.abort(); // Автоматически removeEventListenerStorage & State
IndexedDB v3
Baseline: 2018-2020
Улучшения:
- Binary keys support
- Getters для key paths
- Лучшая производительность
const request = indexedDB.open('MyDatabase', 3);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('users')) {
const store = db.createObjectStore('users', { keyPath: 'id' });
store.createIndex('email', 'email', { unique: true });
}
};
request.onsuccess = (event) => {
const db = event.target.result;
const tx = db.transaction('users', 'readwrite');
const store = tx.objectStore('users');
store.add({ id: 1, name: 'John', email: 'john@example.com' });
};Media APIs
Picture-in-Picture API
Baseline: Safari 13.1 (март 2020), Chrome 70 (октябрь 2018)
const video = document.querySelector('video');
// Включить PiP
video.requestPictureInPicture().then((pipWindow) => {
console.log('PiP width:', pipWindow.width);
});
// Обработчики событий
video.addEventListener('enterpictureinpicture', () => {
console.log('Вошли в PiP режим');
});
video.addEventListener('leavepictureinpicture', () => {
console.log('Вышли из PiP режима');
});
// Выйти из PiP
document.exitPictureInPicture();Network APIs
Fetch API improvements
Keepalive requests (2018):
// Запрос не прервётся при закрытии страницы
fetch('/analytics', {
method: 'POST',
body: data,
keepalive: true,
});Streams API baseline (2020-2021):
const response = await fetch('/large-file');
const reader = response.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
console.log('Получено байт:', value.length);
processChunk(value);
}Security & Permissions
Permissions API
Baseline: ~2019-2020
const result = await navigator.permissions.query({ name: 'geolocation' });
if (result.state === 'granted') {
// Разрешение уже дано
} else if (result.state === 'prompt') {
// Будет показан prompt
} else {
// Отказано
}
// Отслеживание изменений
result.addEventListener('change', () => {
console.log('Статус изменился:', result.state);
});Feature Policy / Permissions Policy
Baseline: 2020-2021
<!-- Старое название: Feature-Policy -->
<iframe src="https://example.com" allow="camera; microphone; geolocation"> </iframe>Permissions-Policy: camera=(), microphone=(), geolocation=(self)Module Systems
ES Modules в браузерах (baseline 2018)
<script type="module">
import { utility } from './utils.js';
utility();
</script>
<script type="module" src="app.js"></script>Dynamic import:
button.addEventListener('click', async () => {
const module = await import('./heavy-feature.js');
module.init();
});Import Maps (конец периода)
Baseline: 2023 (НЕ baseline в 2018-2022)
Safari: 16.4 (март 2023), Chrome: 89 (март 2021)
<script type="importmap">
{
"imports": {
"lodash": "/node_modules/lodash-es/lodash.js",
"react": "https://esm.sh/react@18"
}
}
</script>
<script type="module">
import _ from 'lodash';
import React from 'react';
</script>№# Депрекации и удаления
AppCache (см. HTML changes)
Timeline: Полное удаление 2020-2021
Замена: Service Workers
Flash Player EOL
Timeline: Декабрь 2020 — январь 2021
Синхронные XMLHttpRequest deprecated
Status: Deprecated на main thread, всё ещё работает
// ⚠️ Deprecated
const xhr = new XMLHttpRequest();
xhr.open('GET', '/api', false); // Синхронный режим
xhr.send();
// ✅ Используйте fetch или async XMLHttpRequestВыводы и практические рекомендации
Что можно использовать сегодня (baseline к концу 2022):
ECMAScript:
- ES2018–ES2022 фичи во всех современных браузерах
- Optional chaining, nullish coalescing (ES2020) — must-use
- Top-level await (ES2022) — в модулях
- Private class fields (ES2022)
Web APIs:
- Intersection Observer, Resize Observer
- Performance APIs (Paint Timing, Element Timing, Layout Shift)
- AbortController / AbortSignal
- Picture-in-Picture API
- Permissions API
Что требует полифиллов или feature detection:
- Import Maps — не baseline до 2023
- Cookie Store API — только Chromium
- File System Access API — только Chromium
Best Practices 2018-2022:
1. Используйте современный JavaScript:
// ✅ Modern
const value = obj?.deeply?.nested?.property ?? 'default';
// ❌ Old
const value = (obj && obj.deeply && obj.deeply.nested && obj.deeply.nested.property) || 'default';2. Отменяемые операции:
const controller = new AbortController();
fetch('/api', { signal: controller.signal });
element.addEventListener('click', handler, { signal: controller.signal });
// Одна отмена для всех
cleanup = () => controller.abort();3. Performance measurement:
// Core Web Vitals tracking
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Send to analytics
analytics.track(entry.name, entry.value);
}
});
observer.observe({ entryTypes: ['paint', 'largest-contentful-paint', 'layout-shift'] });Источники:
TC39 Proposals: https://github.com/tc39/proposals
MDN Web Docs: https://developer.mozilla.org
web.dev: https://web.dev
Can I Use: https://caniuse.com
Research ID:
frontend-baseline-2018-2022Дата создания: 17.11.2025
Методология: DeepResearch Agent