AbortSignal.timeout() — встроенный способ задавать таймаут для fetch!
У
Долгое время стандартным решением был
Подход полностью рабочий, но даже для базового таймаута требует дополнительной логики: создание контроллера, запуск таймера и последующую очистку.
С появлением
В этом случае
Для типового случая этого обычно достаточно. Обработка ошибок при этом остаётся стандартной.
Если запрос был прерван через
Механизм работает не только с
Отдельно стоит упомянуть
В таком случае операция завершится при срабатывании первого сигнала — либо по ручной отмене, либо по таймауту.
🔥 Если в проекте до сих пор используется ручная реализация таймаута через
📣 JS Ready | #практика
У
fetch нет встроенного параметра для ограничения времени ожидания. Если сервер отвечает слишком долго или соединение зависает, запрос может оставаться активным неопределённое время.Долгое время стандартным решением был
AbortController в сочетании с setTimeout().const controller = new AbortController();
const timeoutId = setTimeout(() => {
controller.abort();
}, 5000);
try {
const response = await fetch("/api/users", {
signal: controller.signal
});
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
} finally {
clearTimeout(timeoutId);
}
Подход полностью рабочий, но даже для базового таймаута требует дополнительной логики: создание контроллера, запуск таймера и последующую очистку.
С появлением
AbortSignal.timeout() эта задача решается значительно проще.const response = await fetch("/api/users", {
signal: AbortSignal.timeout(5000)
});
const data = await response.json();
console.log(data);
В этом случае
AbortSignal автоматически перейдёт в состояние aborted через указанное количество миллисекунд, после чего операция будет отменена.Для типового случая этого обычно достаточно. Обработка ошибок при этом остаётся стандартной.
try {
const response = await fetch("/api/users", {
signal: AbortSignal.timeout(5000)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === "TimeoutError") {
console.log("Время ожидания истекло");
} else {
console.error(error);
}
}
Если запрос был прерван через
AbortSignal.timeout(), ошибка обычно имеет имя TimeoutError. Если отмена произошла вручную через AbortController.abort(), чаще это будет AbortError. Это даёт возможность разделять сценарии таймаута и ручной отмены на уровне обработки ошибок.Механизм работает не только с
fetch. Любой API с поддержкой AbortSignal может использовать тот же подход.await someAsyncOperation({
signal: AbortSignal.timeout(10000)
});
Отдельно стоит упомянуть
AbortSignal.any(), который позволяет объединять несколько сигналов отмены.const controller = new AbortController();
const signal = AbortSignal.any([
controller.signal,
AbortSignal.timeout(5000)
]);
await fetch("/api/users", { signal });
В таком случае операция завершится при срабатывании первого сигнала — либо по ручной отмене, либо по таймауту.
AbortController и setTimeout, сейчас во многих случаях её можно упростить.Please open Telegram to view this post
VIEW IN TELEGRAM
❤13👍6🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
На сайте собрана большая база материалов по веб-разработке: подробные уроки по HTML5, CSS3, JavaScript и jQuery, а также удобные справочники по HTML- и CSS-свойствам. Здесь можно найти разбор тегов, форм, анимаций, Flexbox, Grid, DOM и других ключевых технологий фронтенда. Большое количество практических примеров, готовых решений и UI-эффектов для интерфейсов.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍6❤5
Берём первый успешный async-результат!
Иногда есть несколько источников данных: основной API, резервный endpoint или кеш. Нужно взять тот, который первым успешно ответит, а не просто завершится первым.
Для этого есть
Он вернёт первый выполненный
В отличие от
Если упали все варианты, тогда будет
🔥 Полезно для резервных API, CDN-зеркал, запасных адресов сервера, загрузки конфигурации и любой логики, где важен первый успешный результат.
📣 JS Ready | #совет
Иногда есть несколько источников данных: основной API, резервный endpoint или кеш. Нужно взять тот, который первым успешно ответит, а не просто завершится первым.
Для этого есть
Promise.any().Promise.any([main, backup, cache])
Он вернёт первый выполненный
Promise и проигнорирует те, которые упали.const response = await Promise.any([
fetch('/api/main'),
fetch('/api/backup')
]);
В отличие от
Promise.race(), он не падает из-за первого reject.Promise.race([...]) // первый завершился — и всё
Promise.any([...]) // ждёт первый успешный
Если упали все варианты, тогда будет
AggregateError.catch (err) {
console.log(err.errors);
}Please open Telegram to view this post
VIEW IN TELEGRAM
❤16🔥5👍4
Парсим URL без try...catch!
Очень часто нужно проверить, является ли строка корректным URL. Обычно для этого используют конструктор URL, но он выбрасывает исключение.
Из-за этого приходится оборачивать код в
Теперь в JavaScript появился
Если строка содержит корректный URL, метод вернёт объект URL.
Если строка некорректна — вместо исключения вернётся
Это позволяет писать обычные проверки без лишней обработки ошибок.
🔥
📣 JS Ready | #совет
Очень часто нужно проверить, является ли строка корректным URL. Обычно для этого используют конструктор URL, но он выбрасывает исключение.
new URL(input);
Из-за этого приходится оборачивать код в
try...catch.Теперь в JavaScript появился
URL.parse().const url = URL.parse(input);
Если строка содержит корректный URL, метод вернёт объект URL.
console.log(url.hostname);
Если строка некорректна — вместо исключения вернётся
null.URL.parse('not a url');
// nullЭто позволяет писать обычные проверки без лишней обработки ошибок.
URL.parse() — удобный способ валидировать и разбирать URL без try...catch, что особенно полезно при работе с пользовательским вводом, API и роутингом.Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤7🔥5
Например,
push() добавляет элементы в массив, map() создаёт новый после обработки данных, а filter() помогает отбирать нужные значения.На картинке — 18 практических задач с решениями по работе с массивами JavaScript: создание, изменение, поиск, удаление, сортировка, объединение, преобразование и обработка данных.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤10🔥5👍3
delete: почему его осторожно используют в производительном коде!
Например:
Выглядит просто, и в большинстве обычных случаев так и есть. Если вы удалили одно свойство из небольшого объекта — скорее всего, никакой разницы по производительности не заметите.
Но ситуация меняется, когда такие операции начинают выполняться тысячи раз подряд или применяются к большим коллекциям объектов.
JavaScript-движки вроде V8 оптимизируют работу с объектами, которые имеют одинаковую структуру. Когда объекты выглядят одинаково, движку проще хранить информацию о них и быстрее выполнять операции доступа к свойствам.
Например:
Все объекты здесь имеют одинаковую форму: у каждого есть
Но если начать менять структуру объектов:
один объект уже отличается от остальных.
Если сделать это массово:
коллекция становится неоднородной.
Для разработчика это обычное изменение данных, но для JavaScript-движка это уже разные варианты объектов, которые сложнее оптимизировать. Особенно это может быть заметно в местах, где вы постоянно работаете с большими массивами одинаковых объектов.
Например:
Допустим, дальше нужно убрать цену у всех товаров:
Технически код правильный. Но если это горячий участок приложения, где такой цикл выполняется часто и на больших объёмах данных, можно оставить структуру объектов прежней:
Теперь поле остаётся частью объекта:
А после
Разница такая:
Например, когда нужно удалить ключ из кэша:
Здесь объект используется как хранилище ключей, и удаление свойства полностью соответствует задаче.
Если структура данных постоянно меняется, часто используют
Ещё один момент, который часто вызывает вопросы:
Например:
После удаления
🔥 Если вы работаете с большими массивами однотипных объектов, большим количеством данных или производительными участками кода, стоит помнить: частое изменение структуры объектов может влиять на оптимизации JavaScript-движка.
📣 JS Ready | #практика
delete — стандартный оператор JavaScript для удаления свойств объекта. На практике он встречается довольно часто: удалить временное поле, убрать ненужный параметр перед отправкой данных, очистить объект после выполнения операции.Например:
const user = {
name: "Анна",
age: 25,
role: "admin"
};
delete user.age;
console.log(user);
// { name: "Анна", role: "admin" }
Выглядит просто, и в большинстве обычных случаев так и есть. Если вы удалили одно свойство из небольшого объекта — скорее всего, никакой разницы по производительности не заметите.
Но ситуация меняется, когда такие операции начинают выполняться тысячи раз подряд или применяются к большим коллекциям объектов.
JavaScript-движки вроде V8 оптимизируют работу с объектами, которые имеют одинаковую структуру. Когда объекты выглядят одинаково, движку проще хранить информацию о них и быстрее выполнять операции доступа к свойствам.
Например:
const users = [
{
id: 1,
name: "Анна",
age: 25
},
{
id: 2,
name: "Иван",
age: 30
},
{
id: 3,
name: "Олег",
age: 28
}
];
Все объекты здесь имеют одинаковую форму: у каждого есть
id, name и age. Такой подход часто встречается в реальных приложениях: списки пользователей, товары, сообщения, записи из базы данных.Но если начать менять структуру объектов:
delete users[0].age;
один объект уже отличается от остальных.
Если сделать это массово:
delete users[0].age;
delete users[1].age;
delete users[2].age;
коллекция становится неоднородной.
Для разработчика это обычное изменение данных, но для JavaScript-движка это уже разные варианты объектов, которые сложнее оптимизировать. Особенно это может быть заметно в местах, где вы постоянно работаете с большими массивами одинаковых объектов.
Например:
const products = data.map(item => ({
id: item.id,
title: item.title,
price: item.price
}));
Допустим, дальше нужно убрать цену у всех товаров:
for (const product of products) {
delete product.price;
}
Технически код правильный. Но если это горячий участок приложения, где такой цикл выполняется часто и на больших объёмах данных, можно оставить структуру объектов прежней:
for (const product of products) {
product.price = undefined;
}
Теперь поле остаётся частью объекта:
console.log("price" in products[0]);
// true
А после
delete оно полностью исчезает:delete products[0].price;
console.log("price" in products[0]);
// false
Разница такая:
undefined — свойство осталось, изменилось только значение; delete — свойство удалено из объекта полностью. Но из этого не следует, что delete нужно запрещать, есть много случаев, где он является правильным решением.Например, когда нужно удалить ключ из кэша:
const cache = {};
cache.users = data;
delete cache.users;
Здесь объект используется как хранилище ключей, и удаление свойства полностью соответствует задаче.
Если структура данных постоянно меняется, часто используют
Map:const cache = new Map();
cache.set("users", data);
cache.delete("users");
Map хорошо подходит для динамических коллекций: кэшей, словарей, временных хранилищ и таблиц соответствий, где ключи регулярно добавляются и удаляются.Ещё один момент, который часто вызывает вопросы:
delete удаляет только свойство объекта, но не сам объект из памяти.Например:
const user = {
settings: {
theme: "dark"
}
};
const settings = user.settings;
delete user.settings;
console.log(settings.theme);
// dark
После удаления
user.settings объект всё ещё существует, потому что переменная settings продолжает хранить на него ссылку. То есть delete убирает связь между объектом и свойством, но не уничтожает данные, на которые ещё кто-то ссылается.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5🔥5
Клонирование объектов без JSON.stringify!
Очень часто для копии объекта используют старый приём:
Но это подходит только для простых JSON-данных и может неожиданно менять структуру объекта.
В современном JavaScript есть нативный способ —
Он создаёт полноценную глубокую копию, сохраняя типы данных.
После копирования остаются настоящие
Также
🔥
📣 JS Ready | #совет
Очень часто для копии объекта используют старый приём:
const copy = JSON.parse(JSON.stringify(obj));
Но это подходит только для простых JSON-данных и может неожиданно менять структуру объекта.
В современном JavaScript есть нативный способ —
structuredClone():const copy = structuredClone(original);
Он создаёт полноценную глубокую копию, сохраняя типы данных.
const user = {
created: new Date(),
roles: new Set(['admin'])
};
const clone = structuredClone(user);После копирования остаются настоящие
Date и Set, а не преобразованные значения.Также
structuredClone() умеет работать с циклическими ссылками:const obj = {};
obj.self = obj;
const copy = structuredClone(obj);structuredClone() — замена JSON-клонированию для глубоких копий объектов без сторонних библиотек и скрытых потерь данных.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥7🤝4
В этой статье:
• Простым языком объясняется, что такое API и зачем он нужен в современных приложениях;
• На примере маркетплейса показывается, как программы обмениваются данными между собой;
• Разбирается разница между UI, frontend, backend и API;
• Показывается, как работают endpoint’ы, JSON и HTTP-запросы;🔊 Продолжай читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥5🤝3
Шпаргалка по циклам в JavaScript!
Например,
На картинке основные конструкции циклов: классический
Сохрани, чтобы не потерять!
📣 JS Ready | #ресурс
Например,
for подходит для ситуаций, когда заранее известно количество итераций, а for...of является способом перебора значений массивов, строк и других итерируемых объектов.На картинке основные конструкции циклов: классический
for, циклы с предварительной и постусловной проверкой while и do...while, перебор коллекций через for...of и for...in, метод массива forEach(), управление выполнением, а также примеры вложенных циклов.Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤6🔥6🤝2
Разбираем, как сделать функцию ограничения значения в диапазоне!
В реальных приложениях часто нужно контролировать значения: громкость плеера, процент выполнения, координаты элемента, количество элементов на странице или любые пользовательские настройки.
Обычно такие проверки пишут через несколько
Создаём функцию
Если значение меньше min, вернётся минимальная граница. Если больше max — максимальная. Во всех остальных случаях вернётся исходное значение. Проверяем работу функции
В первом случае значение превышает максимум, поэтому результат становится 100. Во втором значение ниже минимума и заменяется на 0. Третий вариант уже находится внутри диапазона и остаётся без изменений.
Ограничиваем прогресс загрузки
Даже если данные пришли с ошибкой и сервер вернул значение больше 100, в интерфейс попадёт корректный процент. Такой подход часто используют перед отображением прогресса, размеров, процентов или других числовых параметров.
Ограничиваем пользовательский ввод
Например, пользователь может ввести 10000 или отрицательное число. Перед отправкой запроса значение приводится к допустимому диапазону:
Это помогает держать бизнес-логику предсказуемой и не размазывать проверки по всему коду.
Ограничиваем координаты элемента
Такой вариант полезен для drag-and-drop, игровых механик или любых интерфейсов, где объект не должен выходить за пределы контейнера. Вместо постоянных проверок границ используется одна универсальная функция.
Добавляем проверку некорректного диапазона
Если перепутать границы:
код сразу сообщит об ошибке. Для небольших скриптов такая проверка обычно не нужна, но в общих утилитах проекта она помогает быстрее находить проблемы.
🔥
📣 JS Ready | #практика
В реальных приложениях часто нужно контролировать значения: громкость плеера, процент выполнения, координаты элемента, количество элементов на странице или любые пользовательские настройки.
Обычно такие проверки пишут через несколько
if, но одинаковая логика быстро начинает повторяться. Для таких задач удобно использовать небольшой паттерн clamp() — он оставляет значение внутри заданного диапазона.Создаём функцию
clampfunction clamp(value, min, max) {
return Math.min(
Math.max(value, min),
max
);
}
clamp() принимает три значения: само число, минимальную и максимальную границу.Если значение меньше min, вернётся минимальная граница. Если больше max — максимальная. Во всех остальных случаях вернётся исходное значение. Проверяем работу функции
console.log(clamp(150, 0, 100));
// 100
console.log(clamp(-20, 0, 100));
// 0
console.log(clamp(50, 0, 100));
// 50
В первом случае значение превышает максимум, поэтому результат становится 100. Во втором значение ниже минимума и заменяется на 0. Третий вариант уже находится внутри диапазона и остаётся без изменений.
Ограничиваем прогресс загрузки
const serverProgress = 135;
const progress = clamp(
serverProgress,
0,
100
);
console.log(progress);
// 100
Даже если данные пришли с ошибкой и сервер вернул значение больше 100, в интерфейс попадёт корректный процент. Такой подход часто используют перед отображением прогресса, размеров, процентов или других числовых параметров.
Ограничиваем пользовательский ввод
const userLimit = Number(input.value);
const limit = clamp(
userLimit,
10,
100
);
fetch(`/api/items?limit=${limit}`);
Например, пользователь может ввести 10000 или отрицательное число. Перед отправкой запроса значение приводится к допустимому диапазону:
10 <= limit <= 100
Это помогает держать бизнес-логику предсказуемой и не размазывать проверки по всему коду.
Ограничиваем координаты элемента
const x = clamp(
mouseX,
0,
containerWidth
);
const y = clamp(
mouseY,
0,
containerHeight
);
Такой вариант полезен для drag-and-drop, игровых механик или любых интерфейсов, где объект не должен выходить за пределы контейнера. Вместо постоянных проверок границ используется одна универсальная функция.
Добавляем проверку некорректного диапазона
function clamp(value, min, max) {
if (min > max) {
throw new Error(
"min cannot be greater than max"
);
}
return Math.min(
Math.max(value, min),
max
);
}
Если перепутать границы:
clamp(50, 100, 0);
код сразу сообщит об ошибке. Для небольших скриптов такая проверка обычно не нужна, но в общих утилитах проекта она помогает быстрее находить проблемы.
clamp() — небольшая функция, но она хорошо подходит для большого количества задач. Вместо повторяющихся условий с if появляется единый способ работы с диапазонами, который проще поддерживать и использовать в разных частях приложения.Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍6🔥5
Например, после ввода URL браузер выполняет цепочку операций: разрешение DNS, установка HTTPS-соединения, отправка HTTP-запроса, получение ресурсов и запуск механизма рендеринга.
На картинке — полный жизненный цикл обработки страницы: от сетевого взаимодействия до построения DOM/CSSOM, выполнения JavaScript Engine и формирования итогового интерфейса.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7🤝5🔥4
Temporal API — замена Date в JavaScript!
Создадим объект календарной даты без привязки к времени:
Работа с датами становится более очевидной:
В отличие от
Для точного момента времени используется другой тип:
Часовые пояса теперь являются отдельной частью модели:
Вместо ручных вычислений смещений
Можно безопасно получать локальные компоненты даты:
Это устраняет большое количество ошибок, которые возникают при использовании
Ещё один практический сценарий — сравнение дат:
Вместо сложных преобразований в миллисекунды используется понятное сравнение:
🔥
📣 JS Ready | #практика
Date существует в JavaScript с первых версий языка и до сих пор используется повсеместно, но у него есть несколько архитектурных проблем: изменяемость объектов, сложная работа с часовыми поясами и неоднозначное поведение при преобразовании дат.Temporal — это новый API, который предлагает более предсказуемую модель работы с датами и временем: отдельные типы для даты, времени, момента во времени и часовых поясов.Создадим объект календарной даты без привязки к времени:
const birthday = Temporal.PlainDate.from(
"1995-08-15"
);
console.log(birthday);
// 1995-08-15
PlainDate хранит только дату: год, месяц и день. Он не зависит от часового пояса и подходит для дней рождения, сроков действия, календарных событий.Работа с датами становится более очевидной:
const nextWeek = birthday.add({
days: 7
});
console.log(nextWeek);
// 1995-08-22
В отличие от
Date, объекты Temporal являются иммутабельными: методы не изменяют исходное значение, а возвращают новый объект.Для точного момента времени используется другой тип:
const now = Temporal.Now.instant();
console.log(now);
// 2026-07-15T12:30:45.123Z
Instant представляет конкретную точку на временной шкале и всегда хранится в UTC. Это удобно для логов, событий, транзакций и любых данных, где важна абсолютная точность.Часовые пояса теперь являются отдельной частью модели:
const zoned = now.toZonedDateTimeISO(
"Europe/Warsaw"
);
console.log(zoned);
Вместо ручных вычислений смещений
Temporal явно связывает момент времени с конкретной временной зоной.Можно безопасно получать локальные компоненты даты:
console.log(zoned.hour);
console.log(zoned.day);
console.log(zoned.month);
Это устраняет большое количество ошибок, которые возникают при использовании
getHours(), getDate() и ручной работе с UTC.Ещё один практический сценарий — сравнение дат:
const start = Temporal.PlainDate.from(
"2026-01-01"
);
const end = Temporal.PlainDate.from(
"2026-12-31"
);
Вместо сложных преобразований в миллисекунды используется понятное сравнение:
console.log(
Temporal.PlainDate.compare(start, end)
);
// -1
Temporal разделяет разные сущности времени, поэтому код становится понятнее: дата, время, момент и часовой пояс больше не смешиваются в одном объекте.Temporal — это переработанная модель работы со временем, которая решает основные проблемы старого Date: мутабельность, часовые пояса и сложные преобразования. Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤6🔥4
Получаем размер данных в байтах без ручных подсчётов!
Когда нужно узнать размер строки или подготовленных данных, многие используют:
Но
В JavaScript есть нативный способ —
Он показывает размер содержимого
Например:
Emoji занимает 2 UTF-16 code units, но в
Полезно перед загрузкой файлов, отправкой данных через API, проверкой лимитов и работой с бинарными данными.
Для файлов ещё проще:
🔥
📣 JS Ready | #совет
Когда нужно узнать размер строки или подготовленных данных, многие используют:
str.length
Но
length считает количество UTF-16 кодовых единиц, а не размер данных в байтах.В JavaScript есть нативный способ —
Blob.const size = new Blob([data]).size;
Он показывает размер содержимого
Blob в байтах.Например:
new Blob(['😀']).size;
// 4
'😀'.length;
// 2
Emoji занимает 2 UTF-16 code units, но в
Blob превращается в 4 байта UTF-8.Полезно перед загрузкой файлов, отправкой данных через API, проверкой лимитов и работой с бинарными данными.
Для файлов ещё проще:
file.size;
Blob.size — простой нативный способ узнать размер данных в байтах без сторонних библиотек.Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤6🔥6
Например, cookies позволяют браузеру хранить небольшие данные на стороне клиента, а sessions переносят хранение пользовательского состояния на сервер, оставляя у клиента только идентификатор сессии.
На картинке — основное отличие между Cookies и Sessions, которое помогает понять, где хранить данные пользователя при разработке систем аутентификации.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤5🔥4
Например, TLS-сертификат подтверждает подлинность сервера, а обмен ключами позволяет безопасно создать сессионный ключ для дальнейшего шифрования данных.
На картинке — основные этапы работы HTTPS: TCP handshake, проверка сертификата через Certificate Authority, обмен ключами, создание защищённой сессии, симметричное шифрование и безопасная передача данных между клиентом и сервером.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍6🔥6
Цифровая подпись данных через Web Crypto API!
Цифровая подпись позволяет проверить не только целостность данных, но и подтвердить, что они были созданы владельцем определённого ключа. В браузере для этого можно использовать встроенный Web Crypto API через
Создадим пару RSA-ключей с алгоритмом RSA-PSS и SHA-256. Приватный ключ будет использоваться для создания подписи, а публичный — для проверки её корректности.
После генерации мы получаем два ключа. Приватный ключ должен оставаться защищённым, потому что его утечка позволит создавать подписи от имени владельца. Публичный ключ можно передавать другим участникам для проверки подписей.
Перед созданием подписи данные необходимо преобразовать в бинарный формат, так как криптографические операции работают с байтами, а не со строками.
Теперь создадим цифровую подпись для подготовленных данных с использованием приватного ключа. Результат можно сохранить вместе с данными и использовать позже для проверки подлинности.
Цифровая подпись не скрывает содержимое сообщения и не заменяет шифрование. Она подтверждает, что данные были подписаны соответствующим приватным ключом и не были изменены после создания подписи.
Для проверки используется публичный ключ, исходные данные и полученная подпись. Метод
Если изменить данные после создания подписи, проверка завершится неудачно, так как подпись была создана для другого содержимого.
Важно понимать разницу между цифровой подписью и шифрованием. Шифрование защищает конфиденциальность данных, а подпись подтверждает их происхождение и целостность. Такие механизмы используются при проверке обновлений программного обеспечения, обмене данными между сервисами, защите конфигураций, API-аутентификации и электронном документообороте.
🔥
📣 JS Ready | #практика
Цифровая подпись позволяет проверить не только целостность данных, но и подтвердить, что они были созданы владельцем определённого ключа. В браузере для этого можно использовать встроенный Web Crypto API через
crypto.subtle, который поддерживает генерацию ключей, создание подписей и их проверку.Создадим пару RSA-ключей с алгоритмом RSA-PSS и SHA-256. Приватный ключ будет использоваться для создания подписи, а публичный — для проверки её корректности.
const keys = await crypto.subtle.generateKey(
{
name: "RSA-PSS",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["sign", "verify"]
);
После генерации мы получаем два ключа. Приватный ключ должен оставаться защищённым, потому что его утечка позволит создавать подписи от имени владельца. Публичный ключ можно передавать другим участникам для проверки подписей.
Перед созданием подписи данные необходимо преобразовать в бинарный формат, так как криптографические операции работают с байтами, а не со строками.
const data = new TextEncoder()
.encode("order=123");
Теперь создадим цифровую подпись для подготовленных данных с использованием приватного ключа. Результат можно сохранить вместе с данными и использовать позже для проверки подлинности.
const signature = await crypto.subtle.sign(
{
name: "RSA-PSS",
saltLength: 32
},
keys.privateKey,
data
);
Цифровая подпись не скрывает содержимое сообщения и не заменяет шифрование. Она подтверждает, что данные были подписаны соответствующим приватным ключом и не были изменены после создания подписи.
Для проверки используется публичный ключ, исходные данные и полученная подпись. Метод
verify() возвращает true, если подпись соответствует переданному содержимому.const valid = await crypto.subtle.verify(
{
name: "RSA-PSS",
saltLength: 32
},
keys.publicKey,
signature,
data
);
console.log(valid);
// true
Если изменить данные после создания подписи, проверка завершится неудачно, так как подпись была создана для другого содержимого.
const changedData = new TextEncoder()
.encode("order=999");
const changed = await crypto.subtle.verify(
{
name: "RSA-PSS",
saltLength: 32
},
keys.publicKey,
signature,
changedData
);
console.log(changed);
// false
Важно понимать разницу между цифровой подписью и шифрованием. Шифрование защищает конфиденциальность данных, а подпись подтверждает их происхождение и целостность. Такие механизмы используются при проверке обновлений программного обеспечения, обмене данными между сервисами, защите конфигураций, API-аутентификации и электронном документообороте.
crypto.subtle показывает, что Web Crypto API — это полноценный криптографический инструмент в браузере, который позволяет работать не только с хэшированием, но и с ключами, цифровыми подписями и проверкой подлинности данных.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤5🔥3🤝1
This media is not supported in your browser
VIEW IN TELEGRAM
Например, Imperative Programming позволяет описывать код пошагово через команды и изменение состояния, Functional Programming строит логику через функции и работу с данными, а OOP организует код вокруг объектов, классов и их взаимодействия.
На картинке — основное отличие между тремя подходами к программированию, которые используются в JavaScript и помогают выбирать правильный стиль при разработке приложений.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤7👍5🔥4
Копируем объект вместе с его логикой, а не только значениями!
Многие используют
Но такой способ переносит только значения свойств. Если у объекта есть
Для полного копирования структуры объекта есть
Он возвращает описание каждого свойства:
Теперь можно создать копию, сохранив оригинальное поведение:
Например,
После копирования:
а не просто сохранённое значение.
🔥
📣 JS Ready | #совет
Многие используют
spread или Object.assign() для копирования объектов:const copy = { ...user };Но такой способ переносит только значения свойств. Если у объекта есть
getter, setter или специальные настройки свойства — они будут потеряны.Для полного копирования структуры объекта есть
Object.getOwnPropertyDescriptors().const descriptors = Object.getOwnPropertyDescriptors(obj);
Он возвращает описание каждого свойства:
{
value: 'Alex',
writable: true,
enumerable: true,
configurable: true
}Теперь можно создать копию, сохранив оригинальное поведение:
const clone = Object.defineProperties(
{},
Object.getOwnPropertyDescriptors(obj)
);
Например,
getter останется getter-ом:const user = {
first: 'Alex',
get name() {
return this.first;
}
};После копирования:
clone.name;
// Alex
а не просто сохранённое значение.
Object.getOwnPropertyDescriptors() полезен при создании библиотек, расширении объектов, работе с классами и когда важно сохранить не только данные, но и поведение объекта.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍7🔥5
В этой статье:
• Показывается, как классическую игру «Змейка» можно полностью переосмыслить, поменяв роли игрока и противников;• Разбирается создание игры на чистом JavaScript и Canvas без фреймворков, сборщиков и готовых игровых движков;• Подробно описывается разработка игровой логики, ИИ змей, адаптация под мобильные устройства и балансировка сложности.Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥8❤6
Разбираем RegExp.escape() — стандартный способ экранирования динамических шаблонов!
При построении регулярных выражений из динамических данных долгое время приходилось писать собственные функции экранирования или использовать сторонние утилиты. Такие реализации редко учитывали все особенности синтаксиса регулярных выражений и часто отличались друг от друга.
Создадим регулярное выражение из динамического значения:
В этом примере экранируется только динамическая часть шаблона. Остальная логика (
Такой подход особенно полезен, когда шаблон состоит из статической и динамической частей:
Без экранирования точка внутри строки интерпретировалась бы как метасимвол регулярного выражения, а не как обычный символ.
Ещё один распространённый сценарий — поиск по произвольному пользовательскому запросу:
В этом случае строка всегда интерпретируется буквально, независимо от специальных символов.
Важно понимать:
🔥
📣 JS Ready | #практика
При построении регулярных выражений из динамических данных долгое время приходилось писать собственные функции экранирования или использовать сторонние утилиты. Такие реализации редко учитывали все особенности синтаксиса регулярных выражений и часто отличались друг от друга.
RegExp.escape() стандартизирует эту задачу. Метод принимает строку и возвращает строку, которую можно безопасно использовать как буквальный фрагмент шаблона регулярного выражения.Создадим регулярное выражение из динамического значения:
const filename = "app.config.js";
const regex = new RegExp(
`^${RegExp.escape(filename)}$`
);
console.log(
regex.test("app.config.js")
);
// true
В этом примере экранируется только динамическая часть шаблона. Остальная логика (
^ и $) остаётся полноценным регулярным выражением.Такой подход особенно полезен, когда шаблон состоит из статической и динамической частей:
const extension = "tar.gz";
const regex = new RegExp(
`\\.${RegExp.escape(extension)}$`
);
console.log(
regex.test("archive.tar.gz")
);
// true
Без экранирования точка внутри строки интерпретировалась бы как метасимвол регулярного выражения, а не как обычный символ.
Ещё один распространённый сценарий — поиск по произвольному пользовательскому запросу:
const query = "[admin]";
const text = "Users: [admin], [guest]";
const regex = new RegExp(
RegExp.escape(query),
"gi"
);
const matches = text.match(regex);
console.log(matches);
// ["[admin]"]
В этом случае строка всегда интерпретируется буквально, независимо от специальных символов.
Важно понимать:
RegExp.escape() предназначен для экранирования обычного текста. Метод принимает только строки — если передать объект RegExp, будет выброшен TypeError. Также не стоит применять его к фрагментам, которые должны остаться частью регулярного выражения.RegExp.escape() предоставляет стандартный и корректный способ экранирования динамических фрагментов регулярных выражений. В современных JavaScript-окружениях собственные утилиты для этой задачи обычно больше не нужны. Если же требуется поддержка старых браузеров или версий Node.js, стоит использовать полифил.Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤5🔥5