String.prototype.matchAll — когда нужно работать со всеми совпадениями и группами!
Обычно для поиска по строке используют
Базовый пример:
На выходе — последовательность результатов, аналогичных
Как ведёт себя
При глобальном поиске (g)
Извлечение структурированных данных:
Работа с индексами
Каждое совпадение содержит позицию (
Важно: нужен флаг g
Если передать RegExp без g, будет ошибка. Корректно:
Также можно передать строку — она будет неявно преобразована в RegExp с флагом g:
Именованные группы
Именованные группы повышают читаемость, особенно при большом количестве групп.
Отличие от
Подход рабочий, но есть нюансы: мутируется
🔥
📣 JS Ready | #практика
Обычно для поиска по строке используют
match или exec, но у них есть ограничения: либо приходится писать цикл с lastIndex, либо поведение с группами зависит от режима работы.matchAll решает это аккуратно — возвращает итератор по всем совпадениям с доступом к группам, без ручного управления состоянием.Базовый пример:
const str = "id=1 id=2 id=3";
const matches = str.matchAll(/id=(\d+)/g);
for (const m of matches) {
console.log(m[1]);
}
// 1 2 3
На выходе — последовательность результатов, аналогичных
exec: это массивы с доступом к группам, индексу и исходной строке. Разница в том, что не нужно вручную крутить цикл и следить за lastIndex.Как ведёт себя
match, здесь часто возникает путаница — match работает по-разному в зависимости от флага g:"id=1".match(/id=(\d+)/);
// ["id=1", "1", ...]
"id=1 id=2".match(/id=(\d+)/g);
// ["id=1", "id=2"]
При глобальном поиске (g)
match возвращает только полные совпадения — без capture groups. Это как раз типичный кейс, где matchAll удобнее.Извлечение структурированных данных:
const str = "x:10 y:20 z:30";
const result = [...str.matchAll(/(\w):(\d+)/g)]
.map(([, key, val]) => [key, Number(val)]);
console.log(Object.fromEntries(result));
// { x: 10, y: 20, z: 30 }
matchAll позволяет сразу получить пары ключ–значение без ручного управления состоянием регулярки.Работа с индексами
const str = "test1 test2";
for (const m of str.matchAll(/\w+\d/g)) {
console.log(m[0], m.index);
}
// test1 0
// test2 6
Каждое совпадение содержит позицию (
index), что удобно для парсинга, подсветки, токенизации.Важно: нужен флаг g
"abc".matchAll(/a/);
// TypeError
Если передать RegExp без g, будет ошибка. Корректно:
[..."abc".matchAll(/a/g)];
Также можно передать строку — она будет неявно преобразована в RegExp с флагом g:
[..."abcabc".matchAll("a")];
Именованные группы
const str = "2026-04-19";
for (const m of str.matchAll(
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/g
)) {
console.log(m.groups.year);
}
Именованные группы повышают читаемость, особенно при большом количестве групп.
Отличие от
exec:const str = "id=1 id=2 id=3";
const re = /id=(\d+)/g;
let m;
while ((m = re.exec(str))) {
console.log(m[1]);
}
Подход рабочий, но есть нюансы: мутируется
lastIndex, поведение зависит от повторного использования RegExp, легко допустить скрытую ошибку.matchAll решает это: он использует внутренний клон регулярки, поэтому не мутирует исходный RegExp и не требует ручного контроля состояния.Please open Telegram to view this post
VIEW IN TELEGRAM
❤10👍6🤝4
This media is not supported in your browser
VIEW IN TELEGRAM
На сайте собраны статьи и интерактивные туториалы по JavaScript, React, CSS-анимациям, Flexbox, Grid и архитектуре интерфейсов. Автор объясняет сложные концепции простым языком и показывает не только как, но и почему это работает.
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍7🤝5
Почему setTimeout в Node.js и браузере возвращает разные типы?
Многие думают, что
В браузере это действительно
Но в Node.js возвращается объект
Из-за этого часто возникают проблемы с TypeScript-типами и universal/fullstack кодом между браузером и Node.js.
Например, такой код уже не универсален:
Правильный универсальный тип:
🔥 Он автоматически подстроится под окружение.
📣 JS Ready | #совет
Многие думают, что
setTimeout всегда возвращает число.const id = setTimeout(fn, 1000);
В браузере это действительно
numeric id.typeof id // 'number'
Но в Node.js возвращается объект
Timeout.typeof id // 'object'
Из-за этого часто возникают проблемы с TypeScript-типами и universal/fullstack кодом между браузером и Node.js.
Например, такой код уже не универсален:
let timerId: number;
timerId = setTimeout(fn, 1000);
Правильный универсальный тип:
let timerId: ReturnType<typeof setTimeout>;
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥8🤝5❤2
Например,
push() добавляет элементы в конец массива, map() создаёт новый массив после преобразования, а splice() умеет удалять и вставлять элементы прямо в исходный массив.На картинке — удобная шпаргалка по основным методам массивов: мутации, поиск, трансформация, immutable-методы ES2023 и статические методы.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤14👍7🔥5
Почему отрицательные индексы в массиве “работают”, но не так как ожидается?
Многие думают, что
Но в JavaScript массив — это обычный объект.
Отрицательный индекс не становится индексом массива, а превращается в обычное свойство объекта.
Поэтому
И методы массива не видят такие значения.
Для нормального доступа с конца есть специальный метод
🔥 Очень неочевидная особенность языка, которая часто всплывает при портировании кода из Python и работе с индексами.
📣 JS Ready | #совет
Многие думают, что
arr[-1] — это доступ к последнему элементу, как в Python.arr[-1]
Но в JavaScript массив — это обычный объект.
Отрицательный индекс не становится индексом массива, а превращается в обычное свойство объекта.
arr['-1'] = 'test'
Поэтому
length вообще не меняется.arr.length // 3
И методы массива не видят такие значения.
arr.map(x => x) // без 'test'
Для нормального доступа с конца есть специальный метод
.at()arr.at(-1)
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤7🔥5
This media is not supported in your browser
VIEW IN TELEGRAM
На сайте собрана полноценная база материалов по JavaScript: от базового синтаксиса и работы с DOM до более сложных тем вроде замыканий, событий, async/await и ООП. Материал построен в формате уроков и практических заданий, поэтому подходит не только для чтения, но и для закрепления знаний на практике.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝11❤8👍5
Почему Object.freeze() не делает объект полностью immutable?
На первый взгляд странно: объект же frozen. Но заморожен только сам config. Свойство api действительно нельзя переназначить, удалить или изменить его descriptor. А вот объект, на который оно ссылается, остаётся обычным mutable-объектом.
То есть вот так уже нельзя:
И тут важный момент: в обычном режиме js просто проигнорирует такую попытку; в "use strict" будет TypeError.
Пример:
Но вложенные объекты всё равно можно менять, если их отдельно не заморозить. Для базовой глубокой заморозки можно написать небольшой хэлпер:
И теперь:
В обычном режиме присваивание будет молча проигнорировано. В "use strict" — упадёт с TypeError.
Важно: это упрощённая версия deepFreeze. В реальном коде могут всплыть Symbol-свойства, non-enumerable свойства, циклические ссылки и другие детали. Для production-версии обычно используют
Но для понимания главной идеи этого достаточно:
🔥 Полезно помнить для конфигов, enum-подобных объектов и любых структур, которые не должны случайно мутироваться во время выполнения.
📣 JS Ready | #практика
Object.freeze() часто воспринимают как "защитить объект от любых изменений". Но есть нюанс: он работает только поверхностно.const config = Object.freeze({
api: { retry: 3 }
});
config.api.retry = 10;
console.log(config.api.retry);
// 10
На первый взгляд странно: объект же frozen. Но заморожен только сам config. Свойство api действительно нельзя переназначить, удалить или изменить его descriptor. А вот объект, на который оно ссылается, остаётся обычным mutable-объектом.
То есть вот так уже нельзя:
config.api = null;
console.log(config.api);
// { retry: 10 }
И тут важный момент: в обычном режиме js просто проигнорирует такую попытку; в "use strict" будет TypeError.
Пример:
"use strict";
const user = Object.freeze({
name: "Alex"
});
user.name = "John";
// TypeError: Cannot assign to read only property 'name'
Но вложенные объекты всё равно можно менять, если их отдельно не заморозить. Для базовой глубокой заморозки можно написать небольшой хэлпер:
function deepFreeze(obj) {
if (obj && typeof obj === "object" && !Object.isFrozen(obj)) {
Object.values(obj).forEach(deepFreeze);
Object.freeze(obj);
}
return obj;
}
const settings = deepFreeze({
api: { retry: 3 }
});
И теперь:
settings.api.retry = 10;
console.log(settings.api.retry);
// 3
В обычном режиме присваивание будет молча проигнорировано. В "use strict" — упадёт с TypeError.
Важно: это упрощённая версия deepFreeze. В реальном коде могут всплыть Symbol-свойства, non-enumerable свойства, циклические ссылки и другие детали. Для production-версии обычно используют
Reflect.ownKeys() и защиту от циклических ссылок через WeakSet.Но для понимания главной идеи этого достаточно:
Object.freeze() не делает объект глубоко immutable. Он замораживает только первый уровень.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍6🤝5
This media is not supported in your browser
VIEW IN TELEGRAM
Sass (.sass only) — добавляет полноценную поддержку indented-синтаксиса Sass: подсветку, автодополнение, навигацию и корректное распознавание .sass-файлов без фигурных скобок и точек с запятой. Помогает комфортно работать со старыми и современными Sass-проектами прямо в редакторе.
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝12👍4🔥3❤1
Создаём управляемый Promise без boilerplate!
Раньше, чтобы получить
Это работает, но создаёт лишние переменные и выглядит менее аккуратно.
Теперь в JavaScript есть нативный
Он сразу возвращает готовый
Особенно удобно для очередей, WebSocket, Stream API, событийной логики и мостов между callback/event API и async-await.
🔥
📣 JS Ready | #совет
Раньше, чтобы получить
resolve и reject снаружи Promise, приходилось писать так:let resolve;
const promise = new Promise(res => {
resolve = res;
});
Это работает, но создаёт лишние переменные и выглядит менее аккуратно.
Теперь в JavaScript есть нативный
Promise.withResolvers():const { promise, resolve, reject } =
Promise.withResolvers();Он сразу возвращает готовый
Promise и функции управления им.resolve('done');
await promise;Особенно удобно для очередей, WebSocket, Stream API, событийной логики и мостов между callback/event API и async-await.
await в примере работает внутри async-функции или ES-модуля с top-level await. Также проверь поддержку Promise.withResolvers() в своей среде выполнения.Promise.withResolvers() убирает boilerplate и делает создание управляемых Promise намного чище и читаемее.Please open Telegram to view this post
VIEW IN TELEGRAM
❤11👍6🔥5
В этой статье:
• Показывается, как TrueType hinting VM можно использовать не только для рендеринга шрифтов, но и как полноценную вычислительную среду;
• Разбирается архитектура запуска 3D-рейкастера внутри TTF-шрифта с использованием JavaScript, bytecode и собственного DSL-компилятора;
• Объясняются неожиданные особенности и ограничения TrueType VM.🔊 Продолжайте читать на Habr!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤5🔥5
Например,
new Date() создаёт объект даты, toISOString() помогает получить дату в UTC-формате, а getTime() — работать с таймстампами и вычислениями.На картинке — методы и операции для работы с датами, временем, UTC и форматированием.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝12👍6❤5
Как красиво выводить списки без ручных запятых?
Часто нужно показать список технологий, авторов, тегов или участников.
Обычно это начинают собирать вручную:
Но в JS есть нативный API —
Он сам расставляет запятые, союзы и учитывает правила языка.
Можно менять тип списка: обычное перечисление, выбор или разделение.
🔥
📣 JS Ready | #совет
Часто нужно показать список технологий, авторов, тегов или участников.
Обычно это начинают собирать вручную:
names.join(', ')Но в JS есть нативный API —
Intl.ListFormat.newIntl.ListFormat('ru').format(names);Он сам расставляет запятые, союзы и учитывает правила языка.
newIntl.ListFormat('en').format(names);
// React, Vue, and SvelteМожно менять тип списка: обычное перечисление, выбор или разделение.
new Intl.ListFormat('ru', {
type: 'disjunction'
}).format(['наличные', 'карта']);
// наличные или картаIntl.ListFormat подходит для тегов, участников, категорий, фильтров, хлебных крошек и любых списков в интерфейсе.Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍5🤝5❤2
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🔥6🤝4
URL.canParse + URL API — проверка URL без try/catch и regex!
Частая задача в JS: проверить URL перед
Обычно многие делают так:
Способ рабочий, но со временем такие проверки начинают размножаться по проекту в десятках мест. Минусы:—
Но в платформе есть нативный способ —
Без исключений и без самописных проверок. Просто получаешь boolean.
Но тут есть важный момент, про который многие забывают — относительные URL. Например:
Это не ошибка метода. Просто относительный путь нельзя распарсить без base URL. Поэтому правильно так:
И потом этот же
Это особенно полезно в SSR, middleware, proxy и серверных обработчиках, где часто работают именно с относительными путями.
Ещё хороший кейс — фильтрация пользовательского ввода.
Но тут есть нюанс,
Например ftp: или даже javascript: тоже могут успешно парситься. Поэтому для API, fetch и редиректов обычно дополнительно проверяют
А для редиректов и proxy этого тоже часто недостаточно — обычно дополнительно проверяют hostname/origin через allowlist.
Ещё один полезный момент — нормализация URL. Очень часто одинаковые ссылки приходят в разном виде:
Через URL API это можно привести к одному формату:
🔥 В итоге: если нужно проверить, разобрать или нормализовать URL — лучше сразу использовать стандартный URL API. Кода меньше, поведение предсказуемее и меньше шансов словить странные баги на парсинге.
📣 JS Ready | #практика
Частая задача в JS: проверить URL перед
fetch, редиректом, сохранением в БД, проксированием или обработкой пользовательского ввода.Обычно многие делают так:
function isValid(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
Способ рабочий, но со временем такие проверки начинают размножаться по проекту в десятках мест. Минусы:—
try/catch ради обычной проверки читается тяжеловато— многие потом начинают дописывать сверху regex— появляются разные реализации одной и той же логики.Но в платформе есть нативный способ —
URL.canParse().URL.canParse("https://example.com");
// true
URL.canParse("not-a-url");
// false
Без исключений и без самописных проверок. Просто получаешь boolean.
Но тут есть важный момент, про который многие забывают — относительные URL. Например:
URL.canParse("/api/users");
// false
Это не ошибка метода. Просто относительный путь нельзя распарсить без base URL. Поэтому правильно так:
URL.canParse(
"/api/users",
"https://example.com"
);
// true
И потом этот же
base используешь при создании URL:const url = new URL(
"/api/users",
"https://example.com"
);
Это особенно полезно в SSR, middleware, proxy и серверных обработчиках, где часто работают именно с относительными путями.
Ещё хороший кейс — фильтрация пользовательского ввода.
const input = [
"https://a.com",
"ftp://b.com",
"invalid"
];
const valid = input.filter(u =>
URL.canParse(u)
);
Но тут есть нюанс,
canParse() проверяет только то, что строка корректно парсится как URL с точки зрения WHATWG URL parser. Это не проверка безопасности и не проверка того, что URL подходит именно для вашего кейса.Например ftp: или даже javascript: тоже могут успешно парситься. Поэтому для API, fetch и редиректов обычно дополнительно проверяют
protocol:function parseHttpUrl(u) {
if (!URL.canParse(u)) {
return null;
}
const url = new URL(u);
return (
url.protocol === "http:" ||
url.protocol === "https:"
)
? url
: null;
}
А для редиректов и proxy этого тоже часто недостаточно — обычно дополнительно проверяют hostname/origin через allowlist.
Ещё один полезный момент — нормализация URL. Очень часто одинаковые ссылки приходят в разном виде:
https://example.com
https://example.com/
Через URL API это можно привести к одному формату:
function normalize(u) {
if (!URL.canParse(u)) {
return null;
}
return new URL(u).href;
}
normalize("https://example.com");
normalize("https://example.com/");
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9👍5🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Сайт охватывает как основы языка, так и более продвинутые темы. Здесь разбираются переменные, функции, объекты, массивы, DOM, события, асинхронность, работа с браузером и др. Материал подаётся в формате последовательных уроков с примерами кода и подробными объяснениями.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🤝6❤5
Например, HTTP/1.1 и HTTP/2 работают через TCP, обеспечивая надёжную доставку данных, а HTTP/3 использует QUIC поверх UDP, что позволяет уменьшить задержки и ускорить загрузку страниц.
На картинке показано, какие популярные протоколы используют TCP, а какие работают через UDP, а также как устроены соединения в современных веб-технологиях.
Сохрани, чтобы не потерять!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍5❤4
Сортировка строк без ручных костылей!
Обычный
Для более понятной сортировки в JS есть
Опция
А
🔥 Полезно для таблиц, списков файлов, версий, артикулов, поиска, сортировки товаров и любого UI, где важен естественный порядок сортировки.
📣 JS Ready | #совет
Обычный
sort() сортирует строки лексикографически, поэтому числа внутри строк идут не так, как ожидает пользователь.['file2', 'file10', 'file1'].sort();
// ['file1', 'file10', 'file2']
Для более понятной сортировки в JS есть
Intl.Collator.const collator = new Intl.Collator('ru', {
numeric: true
});Опция
numeric: true заставляет сравнивать числа внутри строк как числа, а не как символы.['v2', 'v10', 'v1'].sort(collator.compare);
// ['v1', 'v2', 'v10']
А
sensitivity: 'base' помогает не цепляться к регистру и мелким различиям при сравнении.new Intl.Collator('ru', {
sensitivity: 'base'
});Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤6🔥6
This media is not supported in your browser
VIEW IN TELEGRAM
На сайте собрана большая коллекция полезных ссылок для разработчиков: учебники, документация, статьи, блоги, инструменты, библиотеки и сервисы по JavaScript, HTML, CSS, React, Node.js и другим технологиям веб-разработки. Всё удобно структурировано по категориям, что позволяет быстро находить нужные материалы для изучения или работы.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7👍5
Object.hasOwn() — способ проверить, есть ли у объекта собственное свойство!
Чаще всего для этого используют
Способ рабочий, но есть один нюанс.
Именно поэтому долгое время надёжным вариантом считался вызов через прототип:
Такой код работает корректно независимо от содержимого объекта, но выглядит довольно громоздко. Сейчас для этой задачи есть отдельный метод:
По сути это более удобная запись той же проверки. Особенно полезен
У таких объектов метода
Ещё один пример — необходимость отличить собственные свойства от унаследованных:
Хотя доступ к
По этой же причине
🔥 Если нужно проверить наличие свойства именно у объекта, а не где-то в его прототипной цепочке,
📣 JS Ready | #практика
Чаще всего для этого используют
hasOwnProperty():const user = {
name: "Анна"
};
console.log(user.hasOwnProperty("name")); // true
Способ рабочий, но есть один нюанс.
hasOwnProperty — обычный метод объекта. Если он был переопределён, результат проверки уже может оказаться неожиданным.const user = {
name: "Анна",
hasOwnProperty: () => false
};
console.log(user.hasOwnProperty("name")); // false
Именно поэтому долгое время надёжным вариантом считался вызов через прототип:
const user = {
name: "Анна"
};
console.log(
Object.prototype.hasOwnProperty.call(user, "name")
); // true
Такой код работает корректно независимо от содержимого объекта, но выглядит довольно громоздко. Сейчас для этой задачи есть отдельный метод:
const user = {
name: "Анна"
};
console.log(
Object.hasOwn(user, "name")
); // true
По сути это более удобная запись той же проверки. Особенно полезен
Object.hasOwn() при работе с объектами без прототипа:const cache = Object.create(null);
cache.users = [];
console.log(
Object.hasOwn(cache, "users")
); // true
У таких объектов метода
hasOwnProperty нет вовсе:const cache = Object.create(null);
console.log(cache.hasOwnProperty);
// undefined
Ещё один пример — необходимость отличить собственные свойства от унаследованных:
const user = {
name: "Анна"
};
console.log(
Object.hasOwn(user, "toString")
); // false
Хотя доступ к
toString есть, само свойство находится в прототипе.По этой же причине
Object.hasOwn() нередко используют внутри for...in, чтобы обрабатывать только собственные свойства объекта.for (const key in data) {
if (Object.hasOwn(data, key)) {
console.log(key);
}
}
Object.hasOwn() сегодня является самым понятным и читаемым решением.Please open Telegram to view this post
VIEW IN TELEGRAM
👍13🔥6❤5🤝3
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍6🤝5😁1