Рефакторинг JavaScript кода: когда и как не сломать проект

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Ваш код работает — но это не значит, что он хорош

Вы написали скрипт. Он запускается. Клиенты довольны. Но проходит месяц, и вы с ужасом смотрите на ту самую функцию, которая разрослась до 200 строк. В ней перемешаны запросы к API, манипуляции с DOM и какая-то магия с датами. Знакомо?

Добро пожаловать в мир, где рефакторинг JavaScript кода — это не прихоть перфекциониста, а жизненная необходимость. Если вы хотите, чтобы ваш проект жил долго и не рассыпался при первой же правке, эта статья для вас.

Что такое рефакторинг и чем он не является

Начнем с главного. Рефакторинг JavaScript кода — это процесс изменения внутренней структуры программы без изменения её внешнего поведения. Вы не добавляете новые функции, не чините баги. Вы просто делаете код чище, понятнее и легче для поддержки.

Многие путают рефакторинг с переписыванием. Это разные вещи. Переписывание — это когда вы выбрасываете старый код и пишете новый с нуля. Рефакторинг — это эволюция, а не революция. Вы меняете код маленькими шагами, каждый раз проверяя, что ничего не сломалось.

Когда нужно проводить рефакторинг JavaScript?

Вот несколько верных признаков того, что ваш код кричит о помощи:

  • Правило трёх. Если вы копируете один и тот же кусок кода в третий раз — пора выносить его в отдельную функцию.
  • Функция-монстр. Функция длиннее 30–40 строк, которую невозможно прочитать за один взгляд.
  • «Магические числа». В коде разбросаны числа вроде 86400000 или 42 без комментариев и констант.
  • Глобальные переменные. Если вы боитесь изменить переменную, потому что не знаете, где ещё она используется.
  • Тесты. Если вы боитесь вносить изменения, потому что нет автоматических тестов — это первый звоночек.

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

Техники рефакторинга JavaScript: от простого к сложному

1. Извлечение функции (Extract Function)

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

// ДО рефакторинга
function calculateInvoice(items) {
  let total = 0;
  for (let item of items) {
    total += item.price * item.quantity;
  }
  let discount = 0;
  if (total > 1000) {
    discount = total * 0.1;
  }
  return total - discount;
}

// ПОСЛЕ рефакторинга
function calculateSubtotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

function calculateDiscount(total) {
  return total > 1000 ? total * 0.1 : 0;
}

function calculateInvoice(items) {
  const subtotal = calculateSubtotal(items);
  const discount = calculateDiscount(subtotal);
  return subtotal - discount;
}

Что изменилось? Теперь каждая функция отвечает за одно действие. Вы можете переиспользовать calculateSubtotal в другом месте, а calculateDiscount легко протестировать отдельно.

2. Замена магических чисел константами

Никогда не используйте «голые» числа в коде. Через месяц вы сами не вспомните, что значит 86400000.

// ПЛОХО
if (Date.now() - lastLogin > 86400000) {
  // запросить повторный вход
}

// ХОРОШО
const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000;
const ONE_DAY_MS = 86400000; // или просто читаемая константа

if (Date.now() - lastLogin > ONE_DAY_MS) {
  // запросить повторный вход
}

3. Упрощение условных конструкций

Сложные вложенные if-else — главный враг читаемости. Используйте ранний возврат (early return) и тернарные операторы.

// ДО
function getDiscount(price, isPremium) {
  let discount;
  if (isPremium) {
    if (price > 500) {
      discount = 0.2;
    } else {
      discount = 0.1;
    }
  } else {
    discount = 0;
  }
  return price * discount;
}

// ПОСЛЕ
function getDiscount(price, isPremium) {
  if (!isPremium) return 0;
  const discount = price > 500 ? 0.2 : 0.1;
  return price * discount;
}

Код стал короче на 40% и гораздо понятнее. Сразу видно: если пользователь не премиум — скидки нет.

4. Замена switch на объект (Replace Switch with Object)

Когда у вас много case, объект с функциями смотрится элегантнее и легче расширяется.

// ДО
function getStatusText(status) {
  switch (status) {
    case 'pending':
      return 'Ожидает';
    case 'approved':
      return 'Одобрено';
    case 'rejected':
      return 'Отклонено';
    default:
      return 'Неизвестно';
  }
}

// ПОСЛЕ
const statusMap = {
  pending: 'Ожидает',
  approved: 'Одобрено',
  rejected: 'Отклонено',
};

function getStatusText(status) {
  return statusMap[status] || 'Неизвестно';
}

5. Избавление от мутаций (Immutability)

Мутирующие функции — источник багов. Старайтесь возвращать новые объекты, а не изменять старые.

// ПЛОХО
function addItem(cart, item) {
  cart.items.push(item);
  return cart;
}

// ХОРОШО
function addItem(cart, item) {
  return {
    ...cart,
    items: [...cart.items, item],
  };
}

Как проводить рефакторинг JavaScript без страха

Рефакторинг без тестов — это как ходить по минному полю с завязанными глазами. Вот пошаговый план безопасного рефакторинга:

  1. Напишите тесты. Если их нет — покройте критически важные участки кода юнит-тестами.
  2. Выберите одну маленькую задачу. Не пытайтесь рефакторить весь проект за раз. Начните с одной функции.
  3. Вносите изменения по одному. Изменили — запустили тесты. Зелёные — идём дальше.
  4. Используйте IDE. Современные редакторы (VS Code, WebStorm) умеют автоматически извлекать функции, переименовывать переменные с учётом контекста.
  5. Commit после каждого шага. Если что-то пошло не так — у вас есть точка отката.

Code Smells в JavaScript: на что обратить внимание

Блоги

Рекомендаций по книгам