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

И вот я с сообщением: «Пожалуйста, не надо. По крайней мере, не вслепую».

Да, это может быть спорным, но должен быть кто-то, кто предлагает альтернативную точку зрения. Упомянутые советы часто основаны на идее: «Что хорошо выглядит, то хорошо». Проблема в том, что это не так. Не всегда.

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

Давайте продемонстрируем несколько примеров «старших» советов по коду JavaScript, которые не обязательно являются серебряной пулей.

Пример первый: только методы массива

// very simplified example of CSV content
const csvRows = [
  { id: 1, value: 10 },
  { id: 2, value: 20 },
  { id: 3, value: 30 },
  { id: 4, value: 40 },
  { id: 5, value: 50 }
];

// "junior" syntax
let sum = 0;

for (const row of csvRows) {
  if (row.value > 20) {
    sum += row.value * 1.21;
  }
}

// "senior" code
const result = csvRows
  .filter(row => row.value > 20)
  .map(row => row.value * 1.21)
  .reduce((acc, value) => acc + value, 0);

Принцип: Замените циклы for/while на методы массива

«Старший» подход кажется более читабельным, но может привести к проблемам с производительностью. Каждый раз, когда вы вызываете метод массива, весь ввод массива (вывод из предыдущего метода массива) повторяется снова.

Если вы соедините три метода массива в цепочку, это будет похоже на написание трех блоков кода цикла «for». Вы бы сделали это без элегантных синтаксических функций?

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

Кроме того, методы массива используют синтаксис обратного вызова. Это потенциально может привести к проблемам с выполнением, когда код смешивается с асинхронными функциями. Использование цикла «for» старой школы упрощает контроль над синхронизацией кода, поскольку требуемый асинхронный код выполняется на шаге итерации.

Пример второй: Объект как переключатель

// ugly code, that is unpopular
  switch (name) {
    case 'Alice':
      return 'Hello, Alice!';
    case 'Bob':
      return 'Hi, Bob!';
    case 'Eve':
      return 'Hey, Eve!';
    default:
      throw new Error(`Unsupported name with value ${name}`);
  }

//... or this version also
  if (name === 'Alice') {
    return 'Hello, Alice!';
  } else if (name === 'Bob') {
    return 'Hi, Bob!';
  } else if (name === 'Eve') {
    return 'Hey, Eve!';
  } else {
    throw new Error(`Unsupported name with value ${name}`)
  }

// and "senior" alternative
function getGreetingUsingObject(name) {
  const greetingMessages = {
    Alice: 'Hello, Alice!',
    Bob: 'Hi, Bob!',
    Eve: 'Hey, Eve!'
  };

  return greetingMessages[name] || throw new Error(`Unsupported name with value ${name}`);
}

Принцип: Используйте объект JavaScript в качестве альтернативы Switch/If-Else.

Нет, пожалуйста, не надо.

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

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

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

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

Напротив, уродливый оператор switch не требует дополнительной памяти вашего приложения. Это логический элемент кода, предназначенный для обработки решения на основе динамических значений и охвата всех возможностей по дизайну. Тот же подход if-else.

Если вы не можете использовать переключатель / if-else в своем коде, попробуйте JavaScript Map.

const greetingsMap = new Map([
  ['Alice', 'Hello, Alice!'],
  ['Bob', 'Hi, Bob!'],
  ['Eve', 'Hey, Eve!']
]);

function greetPerson(name) {
  const greeting = greetingsMap.get(name)
  
  if (!greeting) 
    throw new Error(`Unsupported name with value ${name}`)
  
  return greeting
}

Карта JavaScript — одна из самых недооцененных частей языка. Эта встроенная коллекция специально разработана для управления динамическими структурами ключ-значение и делает это быстро. Ключ даже не обязательно должен быть строкой, это зависит от вас.

Карты потребляют память подобно объектам. Но представляют собой сложный инструмент для динамических отношений ключ-значение.

Всякий раз, когда вы извлекаете данные из карты, она учитывает возможность отсутствия значения под ключом. По этой причине «Map‹T›.get()» возвращает тип «T | неопределенный". Этот дизайн побуждает вас учитывать возможность отсутствия значения каждый раз, когда вы извлекаете данные из карты.

Пример третий: тернарные операторы повсюду

// ugly version
function checkPaymentMethodIfElse(paymentMethod) {
    if (paymentMethod === "cash") {
        return "Payment method is cash.";
    } else if(paymentMethod === "card") {
        return "Payment method is card."; 
    } else {
        throw new Error("Unknown payment method")
    }
}

// ternary version
function checkPaymentMethodTernary(paymentMethod) {
    return paymentMethod === "cash" ? "Payment method is cash." : "Payment method is card.";
}

Принцип: Замените блок if-else тройным оператором.

Использование тернара — неплохой выбор. Но важно понимать, что тернарник предназначен для ситуаций, когда вывод всегда будет истина/ложь и никогда ничего другого. Например, вопрос «Подключена ли база данных?»

Иллюстрация способа оплаты представляет собой плохое применение тернарного оператора в коде JavaScript. В нем описывается ситуация, когда способы оплаты в настоящее время ограничены только «наличными» или «картой». Аналогично обстоятельствам запуска интернет-магазина с ограниченными возможностями оплаты.

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

Кроме того, пример кода открывает пространство для серьезной проблемы. Если метод оплаты из аргумента функции недействителен, троичная версия выберет опцию «не наличными» независимо от того, какое значение будет передано. Другими словами, тернарный оператор не может работать должным образом, используя значения по умолчанию в случае недопустимых значений.

Эти причины показывают, почему вариант if-else или переключатель или версия карты могут рассматриваться как лучший выбор.

Краткое содержание

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

Не переоценивайте усилия по написанию кода, красивого для других людей. Ваш репозиторий приложений не похож на книгу-бестселлер. Хотя удобочитаемость важна, она не является основной целью кода и не является абсолютным критерием различия между «младшим» и «старшим» программистом.

Вы U-S-E указываете компьютеру, как решить проблему. Синтаксис кода должен в первую очередь служить цели достижения наиболее эффективного решения. Когда приложение требует высокой производительности, эстетичный синтаксис кода отходит на второй план. Вот почему программисты проводят обзоры кода, а не превью.