Dev Highlights

Доки по разработке

This project is maintained by teniryte

JS и DOM

Этот файл — короткий учебник по базовым возможностям JavaScript в браузере: работа с query‑строкой URL, новыми методами массивов, промисами, коллекциями, а также основами доступности (WAI‑ARIA).

Работа с URLSearchParams

URLSearchParams позволяет удобно читать и изменять query‑строку (?page=1&name=John) как словарь с методами для добавления, удаления и чтения параметров. Важно, что объект не меняет текущий URL сам по себе — вы управляетесь только строкой параметров.

Основные операции:

// Представим, что адрес: https://example.com/?page=1
const params = new URLSearchParams(window.location.search);

// Чтение
console.log(params.get('page')); // "1"

// Добавление повторяющихся параметров
params.append('tag', 'js');
params.append('tag', 'react');

// Удаление параметра
params.delete('page');

// Замена (если параметр уже есть — перезапишется)
params.set('page', '2');

// Получение всех значений одного ключа
console.log(params.getAll('tag')); // ["js", "react"]

// Новая строка query, которую можно использовать в history.pushState
const newQuery = params.toString(); // "tag=js&tag=react&page=2"

Оператор объединения с null (??) и присваивание ??=

Оператор ?? подставляет запасное значение только в двух случаях — когда левый операнд равен null или undefined. В отличие от ||, он не считает 0, пустую строку '', false и NaN “плохими” значениями.

??= — это краткая запись x = x ?? value, то есть “если x сейчас null/undefined, положить туда значение по умолчанию”.

// Пример с ?? — безопасная подстановка значения по умолчанию
const input1 = null;
const input2 = 0;

const value1 = input1 ?? 10; // 10, потому что input1 === null
const value2 = input2 ?? 10; // 0, потому что 0 — валидное значение

// Пример с ??=
let count: number | undefined;

count ??= 1; // было undefined → станет 1
count ??= 5; // уже 1 → останется 1

Иммутабельные изменения массива: Array.prototype.with

Метод .with(index, value) возвращает новый массив с изменённым элементом по индексу, не мутируя исходный. Это удобно в React и вообще в функциональном стиле, где важно сохранять неизменяемость данных.

const original = [10, 20, 30];

const updated = original.with(1, 200);

console.log(original); // [10, 20, 30] — не изменился
console.log(updated);  // [10, 200, 30] — новый массив

Такой подход легко комбинируется с map/filter и хорошо подходит для обновления состояния в React.

matchMedia и медиазапросы в JS

window.matchMedia позволяет в JS реагировать на CSS‑медиазапросы (ширина окна, prefers‑color‑scheme и т.п.). Вы получаете объект, у которого есть:

Важно всегда снимать обработчики, чтобы избежать утечек.

// Проверяем, узкий ли экран (например, телефон)
const media = window.matchMedia('(max-width: 600px)');

function handleChange() {
  console.log('Текущий статус:', media.matches ? 'узкий экран' : 'широкий экран');
}

// Первый запуск
handleChange();

// Подписка на изменения
media.addEventListener('change', handleChange);

// Где‑то при размонтировании/очистке:
media.removeEventListener('change', handleChange);

На основе этого паттерна легко написать свои хуки (useMediaQuery) или отдельный стор статуса медиа.

Основы Promise

Promise описывает результат асинхронной операции. У него три состояния:

После создания Promise вы обрабатываете результат через .then, .catch и .finally.

function wait(ms: number): Promise<string> {
  return new Promise((resolve, reject) => {
    if (ms < 0) {
      reject(new Error('Ожидание не может быть отрицательным'));
      return;
    }

    setTimeout(() => {
      resolve(`Ждали ${ms} мс`);
    }, ms);
  });
}

wait(1000)
  .then(result => {
    console.log('Успех:', result);
  })
  .catch(error => {
    console.error('Ошибка:', error.message);
  })
  .finally(() => {
    console.log('Всегда вызывается в конце, независимо от результата');
  });

Комбинаторы Promise

Частые паттерны работы с несколькими промисами:

const p1 = wait(500);
const p2 = wait(1000);

Promise.all([p1, p2]).then(([r1, r2]) => {
  console.log('Оба завершились:', r1, r2);
});

Promise.race([p1, p2]).then(first => {
  console.log('Первый результат:', first);
});

Коллекции Map и Set

Map — ассоциативный массив с любыми ключами

Map хранит пары ключ/значение и хорошо подходит для кешей, словарей и таблиц соответствий. В отличие от обычного объекта, ключом может быть что угодно: строка, число, объект.

type User = { id: number; name: string };

const cache = new Map<string, User>();

const user: User = { id: 1, name: 'Alice' };

cache.set(`user-${user.id}`, user);

if (cache.has('user-1')) {
  console.log('Из кеша:', cache.get('user-1'));
}

console.log(cache.size); // количество записей

Set — множество уникальных значений

Set хранит только уникальные значения. Хороший практический пример — удаление дубликатов из массива и быстрая проверка принадлежности.

const raw = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(raw)];

console.log(unique); // [1, 2, 3]

const ids = new Set<number>();
ids.add(10);
ids.add(10); // повтор не добавится
console.log(ids.has(10)); // true

Основы WAI‑ARIA и доступности

WAI‑ARIA — набор ролей и атрибутов, которые помогают скринридерам и другим ассистивным технологиям корректно понимать интерфейс. Главное правило: сначала используйте семантический HTML (<button>, <nav>, <header>), а ARIA — только чтобы восполнить пробелы.

Частые атрибуты

// Кнопка, управляющая раскрытием панели
<button
  aria-expanded={isOpen}
  aria-controls="details"
  onClick={() => setIsOpen(prev => !prev)}
>
  Подробнее
</button>

<div
  id="details"
  role="region"
  aria-hidden={!isOpen}
>
  Содержимое панели
</div>

Важно: многие ARIA‑атрибуты логичны только вместе с правильной ролью. Например, aria-checked ожидается на role="checkbox" или нативном <input type="checkbox">.

Дополнительные приёмы JS

Стрелочные и обычные функции

Обычные функции (function foo() {}):

Стрелочные функции (const foo = () => {}):

const obj = {
  count: 0,
  incClassic: function () {
    this.count += 1; // this указывает на obj
  },
  incArrow: () => {
    // this здесь берётся из внешнего контекста, а не из obj
  },
};

Для методов объектов и классов чаще используют обычные функции, а стрелки — для колбэков и замыканий.

Object.freeze — защита объекта от изменений

Object.freeze делает объект “замороженным”: нельзя изменять существующие свойства, добавлять новые или удалять старые. Это поверхностная заморозка (вложенные объекты останутся изменяемыми).

const config = Object.freeze({
  apiUrl: 'https://api.example.com',
  retries: 3,
});

// В строгом режиме это вызовет ошибку
// config.apiUrl = 'https://another.api'; // TypeError

console.log(config.apiUrl); // 'https://api.example.com'

Унарный минус

Унарный минус (-x) меняет знак числа. Полезно, когда нужно быстро инвертировать значение или, например, сортировать по убыванию.

const x = 5;
const y = -x; // -5

const nums = [1, 3, 2];

// Сортировка по убыванию
nums.sort((a, b) => b - a);
console.log(nums); // [3, 2, 1]