JavaScript Ready | Программирование
8.18K subscribers
987 photos
60 videos
524 links
Авторский канал по разработке на JavaScript.
Ресурсы, обучения, задачи, шпаргалки.
Ежедневно информация пополняется!

Cотрудничество: @energy_c
Download Telegram
📱 Proxy API — перехват операций с объектами и функциями!

Разбираем Proxy API, с помощью которого можно перехватывать и переопределять базовые операции с объектами и функциями: контролировать доступ и изменение данных, управлять видимостью и перечислением свойств, отслеживать вызовы функций и создание экземпляров. Используется для валидации, логирования, метапрограммирования и создания гибкого поведения объектов.

📣 JS Ready | #шпора
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥9🤝4❤2
📂 Напоминалка по циклам в JavaScript!

Например, for удобно использовать, когда заранее известно количество итераций, а for...of — когда нужно пройтись по массиву или строке.

На картинке — 5 циклов, которые постоянно встречаются в коде.

Сохрани, чтобы не забыть!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13👍8🤝3❤2
Создаём индекс данных из массива для быстрого поиска через Map!

При работе с данными из API часто требуется получать элементы массива по идентификатору. Самый простой вариант — использовать find(), но при большом количестве повторных поисков такой подход приводит к постоянному перебору коллекции.

Например, поиск пользователя по id:
const users = [
{
id: 1,
name: "Alex"
},
{
id: 2,
name: "John"
}
];

const user = users.find(
user => user.id === 2
);

console.log(user);


Метод find() последовательно проверяет элементы массива и возвращает первое совпадение.

Для единичного поиска этого достаточно. Но если выполнять такие операции внутри циклов или часто обращаться к одним и тем же данным, появляется лишняя нагрузка: каждый вызов снова проходит массив от начала до нужного элемента.

Вместо этого можно заранее создать индекс, где идентификатор будет использоваться как ключ, а объект — как значение:
const usersById = new Map(
users.map(user => [
user.id,
user
])
);

console.log(
usersById.get(2)
);


Теперь получение пользователя выполняется через ключ без повторного перебора массива.

По сложности подходы отличаются:
find() - O(n)
Map.get() - O(1) в среднем


Поэтому такой паттерн особенно полезен при работе с большими коллекциями и частыми операциями чтения.

Например, пользователи и их настройки могут приходить отдельными запросами:
const settings = [
{
userId: 1,
theme: "dark"
},
{
userId: 2,
theme: "light"
}
];

const settingsByUser = new Map(
settings.map(item => [
item.userId,
item
])
);


После создания индекса можно собрать итоговую структуру данных без вложенных поисков внутри массива:
const result = users.map(user => ({
...user,
settings: settingsByUser.get(user.id)
}));

console.log(result);


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

Для создания индекса также можно использовать обычный объект:
const usersById = Object.fromEntries(
users.map(user => [
user.id,
user
])
);

console.log(usersById[2]);


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

Важно учитывать, что индекс — это дополнительная структура в памяти. Если нужен один поиск, обычный find() может быть проще и понятнее. Но при многократном доступе к данным создание индекса позволяет значительно сократить количество лишних операций.

🔥 Создание индекса из массива позволяет заранее подготовить данные для быстрого доступа и избежать повторных линейных поисков при обработке коллекций.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
❤12👍6🔥5
📂 Напоминалка по тестированию API!

Например, Smoke Testing проверяет, что критичные эндпоинты вообще доступны, а Contract Testing помогает убедиться, что запросы и ответы по-прежнему соответствуют API-контракту.

На картинке — 9 основных видов API-тестирования: от функциональных и интеграционных тестов до нагрузочного, security- и fuzz-тестирования.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍7🤝3❤1
JavaScript умеет освобождать ресурсы сам, без finally!

Когда код временно открывает ресурс, соединение, файл, lock или подписку — его обязательно нужно закрыть даже при ошибке. Обычно для этого пишут try/finally:
const connection = openConnection();

try {
await connection.query(sql);
} finally {
connection.close();
}


Современный JS поддерживает явное управление ресурсами. Объект может определить Symbol.dispose, а using гарантированно вызовет его при выходе из области видимости:
function openConnection() {
return {
query(sql) {
return db.query(sql);
},

[Symbol.dispose]() {
db.close();
}
};
}


После этого управление жизненным циклом ресурса становится некой частью языка:
{
using connection = openConnection();

await connection.query(sql);
}
// connection[Symbol.dispose]() уже вызван


Для ресурсов с асинхронной очисткой существует отдельная пара await using и Symbol.asyncDispose:
await using connection = await connect();

await connection.query(sql);

// await connection[Symbol.asyncDispose]()


🔥 using полезен для ресурсов с чётким временем жизни: файлов, соединений, блокировок и временных подписок. Жизненным циклом управляет сам scope.

📣 JS Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
❤8🔥5🤝4
📂 Напоминалка по архитектурным концепциям!

Например, балансировщик нагрузки распределяет входящий трафик между серверами, кэширование снижает нагрузку на базу данных, а очереди сообщений позволяют сервисам работать асинхронно и меньше зависеть друг от друга.

На картинке — 12 архитектурных концепций, которые полезно знать разработчику: от CDN и API Gateway до шардинга, consistent hashing и автомасштабирования.

Сохрани, чтобы была под рукой!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🤝8❤5
📂 Шпаргалка по Spread и Rest!

Одинаковый синтаксис ..., но разные задачи: Spread раскрывает элементы и свойства, а Rest собирает оставшиеся значения в массив или объект.

На картинке — различия, синтаксис и примеры использования Spread и Rest при работе с массивами, объектами и функциями.

Сохрани как краткую памятку!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥9🤝4❤1
Array.prototype.fill() — заполнение массивов и особенности работы со ссылками!

Array.prototype.fill() — часто используемый метод, который заменяет элементы массива указанным значением. Он изменяет исходный массив и применяется при подготовке структур данных, создании массивов фиксированной длины и инициализации состояний. Пример:
const numbers = new Array(5).fill(0);

console.log(numbers);
// [0, 0, 0, 0, 0]


Метод принимает три аргумента:
array.fill(value, start, end);


value — значение для заполнения, start — индекс начала изменения, end — граница диапазона без включения самого индекса. Например:
const values = [10, 20, 30, 40, 50];

values.fill(1, 2, 4);

console.log(values);
// [10, 20, 1, 1, 50]


Один из распространённых вариантов использования — создание массивов состояний:
const visited = new Array(100).fill(false);

visited[25] = true;


Такой подход используется в алгоритмах с индексами, графах, таблицах состояний и буферных структурах.

Главная особенность fill() проявляется при работе с объектами. Метод не создаёт отдельные экземпляры, а записывает одно и то же значение во все позиции массива:
const items = new Array(3).fill({
active: false
});

items[0].active = true;

console.log(items);


Результат:
[
{ active: true },
{ active: true },
{ active: true }
]


Все элементы ссылаются на один объект. Для создания независимых значений необходимо генерировать объект для каждой позиции:
const items = Array.from(
{ length: 3 },
() => ({
active: false
})
);


Также fill() изменяет разреженные массивы, создавая реальные индексные свойства:
const array = new Array(3);

console.log(Object.keys(array));
// []

array.fill(null);

console.log(Object.keys(array));
// ["0", "1", "2"]


Метод поддерживает отрицательные индексы:
const numbers = [1, 2, 3, 4, 5];

numbers.fill(0, -2);

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


Array.prototype.fill() не выполняет копирование значений. Для примитивов поведение обычно ожидаемое, но при работе с объектами и массивами необходимо учитывать ссылочную модель JavaScript. Метод, который хорошо показывает важный принцип языка: в JavaScript значение и ссылка на объект — это разные вещи.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝8❤6👍4
📱 Event Sourcing: состояние как история событий!

Event Sourcing меняет привычный подход к хранению данных: вместо перезаписи текущего состояния система сохраняет последовательность событий, из которых это состояние можно восстановить.

В этом посте:
• Разберём, почему в Event Sourcing состояние становится производным;

• Отделим команды от событий и поймём их разные роли;

• Реализуем восстановление состояния через последовательный replay событий;

• Разберём, почему события становятся источником истины, а не просто журналом изменений.


Такой подход сохраняет не только результат изменений, но и их причинную историю, позволяя строить новые представления данных и воспроизводить состояние системы на основе событий.

📣 JS Ready | #гайд
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍6❤3
Получаем только изменённые поля объекта!

При изменении состояния приложения можно сравнить предыдущую и текущую версию объекта, чтобы определить только обновлённые значения. Такой diff позволяет отделить изменившиеся поля от тех, которые остались без изменений, и использовать только актуальные данные для дальнейшей обработки.
const oldUser = {
name: "Alex",
age: 30,
role: "developer"
};

const newUser = {
name: "Alex",
age: 31,
role: "developer"
};


Для получения изменённых полей можно преобразовать объект в массив пар через Object.entries(), отфильтровать только отличающиеся значения и собрать новый объект обратно с помощью Object.fromEntries().
const changes = Object.fromEntries(
Object.entries(newUser)
.filter(([key, value]) =>
!Object.is(value, oldUser[key])
)
);

console.log(changes);


В результате получится объект, содержащий только изменённые поля:
{
age: 31
}


Такой подход применяется при обновлении состояния во frontend-приложениях, работе с формами и подготовке частичных изменений для API через PATCH запросы.

Вместо отправки всей структуры можно передать только те значения, которые действительно были изменены.
const hasChanges =
Object.keys(changes).length > 0;

console.log(hasChanges);


Проверка через количество ключей позволяет определить, были ли изменения вообще. Если объект changes пустой, значит исходное и новое состояние полностью совпадают.

Важно учитывать, что данный способ выполняет только поверхностное сравнение (shallow diff) и проверяет значения первого уровня объекта.
const first = {
profile: {
name: "Alex"
}
};

const second = {
profile: {
name: "Alex"
}
};

console.log(first.profile === second.profile);

// false


Вложенные объекты сравниваются по ссылке, поэтому одинаковая структура внутри разных объектов не делает их равными. Для таких случаев требуется использовать глубокое сравнение (deep comparison) или отдельную логику построения diff.

🔥 Также при реализации реальных обновлений через PATCH нужно отдельно учитывать удаление полей, так как простой shallow diff определяет только добавленные и изменённые значения.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7👍4🤝1
📂 Напоминалка для работы с async/await!

Например, await приостанавливает выполнение текущей async-функции до завершения Promise, а try...catch позволяет удобно обрабатывать возникающие ошибки.

На картинке — основной синтаксис async/await, принцип его работы, сравнение с цепочками .then() и другие полезные моменты.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9❤7👍7
Склеивайте множество синхронных обновлений в одно!

Иногда функция может вызываться десятки раз за один проход синхронного кода.

Можно накопить изменения и запланировать обработку один раз:
const pending = new Set();

function update(id) {
pending.add(id);
scheduleFlush();
}


queueMicrotask() выполнит flush после текущего синхронного кода, но до перехода к следующей macrotask:
function flush() {
scheduled = false;

process([...pending]);
pending.clear();
}


Теперь даже серия вызовов обработается одной пачкой:
update(1);
update(2);
update(3);

// process([1, 2, 3]) — один раз


🔥 Такой подход объединяет множество быстрых обновлений в одну обработку, уменьшая лишние вычисления и нагрузку. Что крайне полезно для событий и часто обновляемых данных.

📣 JS Ready | #совет
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10❤6👍5
📂 Напоминалка по деструктуризации!

Например, const [a, b] = arr позволяет получить значения из массива, а const { name, age } = user — извлечь нужные свойства из объекта.

На картинке — основные способы деструктуризации массивов и объектов, которые пригодятся в повседневной разработке.

Сохрани, чтобы не потерять!

📣 JS Ready | #ресурс
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍8❤3🤝2
Почему Array.sort() может создать баг далеко от места сортировки!

С sort() проблема не в самой сортировке, а в скрытой мутации. Вызов выглядит как обычное получение отсортированного результата, хотя одновременно изменяет сам исходный массив:
const scores = [30, 10, 20];

const sorted = scores.sort(
(a, b) => a - b
);

console.log(scores);
// [10, 20, 30]


sort() сортирует массив непосредственно и возвращает ту же ссылку. Поэтому изменение увидит не только код, работающий с sorted, но и любой код, который уже хранит ссылку на исходный массив:
const scores = [30, 10, 20];
const cached = scores;

scores.sort((a, b) => a - b);

console.log(cached);
// [10, 20, 30]


Особенно неприятно это становится на границе функций. По названию getSortedUsers() можно ожидать получение нового результата, но реализация незаметно меняет массив вызывающего кода:
function getSortedUsers(users) {
return users.sort(
(a, b) => a.age - b.age
);
}


В результате функция имеет побочный эффект, который не очевиден из места её вызова. Это уже может затронуть состояние, кэш, мемоизацию или другой код, использующий тот же массив:
const users = getUsers();
const original = users;

getSortedUsers(users);

console.log(original === users);
// true


Классический способ избежать этого — сначала создать новый массив и сортировать уже его. Такой код работает, но копирование приходится выражать отдельной операцией:
const sorted = [...scores].sort(
(a, b) => a - b
);

console.log(scores);
// [30, 10, 20]


toSorted() делает намерение явным: получить новый массив с другим порядком элементов, не изменяя исходный. При этом возвращается уже другая ссылка:
const sorted = scores.toSorted(
(a, b) => a - b
);

console.log(scores);
// [30, 10, 20]

console.log(scores === sorted);
// false


Важно, что копируется именно массив. Объекты внутри него не клонируются, поэтому обе коллекции по-прежнему содержат ссылки на одни и те же объекты:
const users = [
{ name: "Alex", age: 30 },
{ name: "Bob", age: 20 },
];

const sorted = users.toSorted(
(a, b) => a.age - b.age
);

console.log(
sorted[0] === users[1]
);
// true


Поэтому изменение самого объекта будет видно через обе коллекции:
sorted[0].age = 25;

console.log(users[1].age);
// 25


Похожая логика есть у toReversed() и toSpliced(): они позволяют выполнить соответствующую операцию без изменения исходного массива, в отличие от мутирующих reverse() и splice().

🔥 Поэтому выбор между sort() и toSorted() — это не просто вопрос синтаксиса, это выбор контракта операции: изменить существующий массив или получить новый, сохранив исходный. Чем больше у массива потребителей, тем важнее эта разница.

📣 JS Ready | #практика
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥9👍6🤝4❤1