Совершенный код: отделяем получение данных от их использования

Читать в полной версии →

Есть такой код, который я называю "код, который заставляет себя переписывать". Этот код не выглядит плохо и про него нельзя сказать сразу, что он делает что-то плохое. Проблемы проявляются позже — в тот момент, когда нужно внести изменения либо отладить его.

Рассмотрим пример. В проектах Хекслета студенты часто пишут подобный код:

document.getElementById('spinner').classList.add('d-none');

На первый взгляд ничего криминального. Здесь извлекается DOM элемент с идентификатором spinner и затем к нему добавляется новый класс d-none.

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

document.getElementById('spinner').classList.add('d-none');
document.getElementById('spinner').classList.remove('d-flex');

Да, не все его так напишут. Но, всё же, как показывает практика, многие просто скопируют строчку и поменяют только последнюю часть.

Сразу бросается в глаза, что произошло дублирование. Причём работа с DOM — недешёвая операция. Такое дублирование приводит к замедлению кода, иногда значительному.

Подписывайтесь на канал Кирилла Мокевнина в Telegram — чтобы узнать больше о программировании и профессиональном пути разработчика

Посмотрев на этот код, можно подумать, что это косяк того программиста, что выполнил дублирование. Отчасти это так, но проблема глубже. Исходная строчка была написана так, что она "заставляет" переписывать код при любом повторном обращении. В этом коде выполняется сразу две операции:

Как правило, первая операция делается ровно один раз, а вот манипуляций может быть много. Причём обычно они появляются не сразу, а накапливаются в процессе жизни кода. Это значит, что код в любом случае придётся переписать.

Поэтому всегда стоит сразу разделять получение данных и их использование:

const spinnerElement = document.getElementById('spinner'); // получение
spinnerElement.classList.add('d-none'); // использование

Не стоит беспокоиться, что кода станет на одну строчку больше. Это тот самый случай, когда "больше — лучше". Во-первых, у выбранных данных появляется имя (переменная). Благодаря именованию сразу понятно, что это такое, с какой сущностью имеем дело. Во-вторых, в этом коде не появилось дополнительной логики. И он легче читается. Ещё один плюс — проще отладка.

Ниже ещё примеры, которые я извлёк из реальных проектов специально для данной статьи.

// Неправильно
const { output } = state.fileTabsInfo.tabs.find(tab => tab.id === 'output').result;

// Правильно
const outputTab = state.fileTabsInfo.tabs.find(tab => tab.id === 'output');
const { output } = outputTab.result;

// Неправильно
const html = document.querySelector('#alertBox').innerHTML;

// Правильно
const alertElement =  document.querySelector('#alertBox'); // получение
const html = alertElement.innerHTML; // использование

// Неправильно
const name = User.find(5).getName();

// Правильно
const user = User.find(5); // получение
const name = user.getName(); // использование

// Неправильно
const newUrl = `${url.parse(address).protocol}//${url.parse(address).host}`;

// Правильно
const urlParts = url.parse(address);
const newUrl = `${urlParts.protocol}//${urlParts.host}`;

Дополнительные материалы: