Легче, чем вы думаете, избавиться от циклов for и написать более мощный код.

Если за последнее десятилетие вы проходили типичный курс программирования в старшей школе или колледже, вас, вероятно, учили базовым структурам программирования, таким как if, for, while и другим стандартным структурам.

Чего вас, вероятно, не учили, так это того, что ни один из них не является необходимым, особенно в современном Javascript (и, следовательно, Typescript).

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

Что не так с for и while?

Сами по себе for и while довольно невиновны. А когда-то, очень давно, когда языки программирования находились в зачаточном состоянии, это также были единственные возможности делать то, что они делают.

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

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

Если это все, что произошло, все может быть в порядке. Однако, поскольку вы создали изменяемый массив или объект за пределами цикла for, вы подвергаете его изменениям в другом месте вашего кода — и именно здесь начинается ущерб.

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

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

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

Что делать вместо этого?

Ответ заключается в написании неизменяемого кода в «функциональном стиле», в котором используются методы прототипов массивов и другие немутативные стратегии.

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

Есть три, которые вы можете считать «всем, что вам нужно», и мы сосредоточимся на них здесь.

  1. .map — перебирает массив, возвращая новый массив. Обычно новый массив является своего рода преобразованием оригинала.
  2. .filter — перебирает массив, возвращая новый массив, содержащий только значения, соответствующие вашим критериям.
  3. .reduce — Самый пугающий, но, возможно, и самый полезный из этих трех основных методов. Это зацикливается на массиве, позволяя вам шаг за шагом создавать что-то совершенно новое, что может быть любым типом данных, который вы хотите, а не только массивом.

Примечание. С помощью элементов .keys, .values или .entries мы также можем использовать их для обработки объектов, которые вы используете в качестве хэш-карт и структур Javascript Map.

Каковы преимущества:

  • Неизменяемость. Вы можете сделать это по крайней мере по шаблону, но также можете реализовать с помощью Object.freeze, чтобы быть полностью уверенным.
  • Улучшенный код с самоописанием. Новой переменной всегда нужно новое имя, что дает возможность уточнить, что должен делать ваш код.
  • Снижение психологических затрат на будущие изменения. Облегчает чтение, понимание и изменение кода.
  • Композиция функций и улучшенная расширяемость. Код функционального стиля предназначен для использования с композицией. (Композиция функций выходит за рамки этой статьи, но вкратце это мощный инструмент, который, возможно, более эффективен, чем наследование ООП.)
  • Тестируемый. Поскольку код разбит на небольшие отдельные задачи, его легче разбить на более мелкие вызываемые функции, которые можно защитить от последующего повреждения с помощью модульных тестов.

ЕСТЬ некоторые недостатки:

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

Как перейти от for к прототипу функционального массива Методы:

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

Вам предлагается попробовать эти стратегии в своем собственном коде, чтобы сравнить их.

Пример 1:

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

Вот оригинал:

const contactList = [];
for (const contact of contacts) {
  const newContact = `"${contact.firstName}","${contact.email}"`;
  console.log(newContact);
  contactList[contactList.length] = newContact;
}
await toCSV(contactList.join("\n"));

А вот как этот же код можно написать без for :

const contactList = contacts.map((contact) => {
  return `"${contact.firstName}","${contact.email}"`;
});
console.log(contactList.join("\n"));
await toCSV(contactList.join("\n"));

Не так жестко, правда?

Пример 2:

Часто мы хотим иметь дело только с подмножеством списка.

В этом примере мы ищем просроченные счета и инициируем электронное письмо получателю платежа.

const today = new Date();
const lateInvoices = [];
for (const invoice of invoices) {
  if (!invoice.status === 'paid' && invoice.paymentDate <= today) {
    lateInvoices.push(invoice);
  }
}
await sendInvoiceEmails(EInvoiceType.LatePayment, lateInvoices);

И вот альтернативный способ написать это:

const today = new Date();
const lateInvoices = invoices.filter(
  (inv) => (!inv.status === 'paid' && inv.paymentDate <= today)
);
await sendInvoiceEmails(EInvoiceType.LatePayment, lateInvoices);

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

Пример 3:

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

const distances = {
  A: 0, // Within 2 miles
  B: 0, // More than 2 miles
};

const grades = {
  K: 0,
  A: 0, // Grade 1 to 3
  B: 0, // Grade 4 to 6
  C: 0, // Grade 7 and 8
};

for (const student of students) {
  const studentDistance = getDistanceBetween(student.addressCoords, schoolCoords);
  if (studentDistance <= 2) {
    distances.A = distances.A + 1;
  } else {
    distances.B = distances.B + 1;
  }
  if (student.grade < 1) {
    grades.K = grades.K + 1;
  } else if (student.grade >= 1 && student.grade <= 3) {
    grades.A = grades.A + 1;
  } else if (student.grade >= 4 && student.grade <= 6) {
    grades.B = grades.B + 1;
  } else if (student.grade >= 7) {
    grades.C = grades.C + 1;
  }
}

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

const startDistances = {
  A: 0, // Within 2 miles
  B: 0, // More than 2 miles
};

const distanceGroupers = [
  { key: 'A', isValid: (distance) => (distance <= 2) },
  { key: 'B', isValid: (distance) => (distance > 2) },
];
const distances = students.reduce((acc, student) => {
  const grouper = distanceGroupers.find((dg) => dg.isValid(
    getDistanceBetween(student.addressCoords, schoolCoords)
  );
  return {
    ...acc,
    [grouper.key]: acc[grouper.key] + 1,
  };
}, startDistances);

const startGrades = {
  K: 0,
  A: 0, // Grade 1 to 3
  B: 0, // Grade 4 to 6
  C: 0, // Grade 7 and 8
};

const gradeGroupers = [
  { key: 'K', isValid: (grade) => (grade < 1) },
  { key: 'A', isValid: (grade) => (grade >= 1 && grade <= 3) },
  { key: 'B', isValid: (grade) => (grade >= 4 && grade <= 6) },
  { key: 'C', isValid: (grade) => (grade >= 7) },
];
const grades = students.reduce((acc, student) => {
  const grouper = distanceGroupers.find((gg) => gg.isValid(student.grade));
  return {
    ...acc,
    [grouper.key]: acc[grouper.key] + 1;
  };
}, startGrades);

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

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

Займитесь программированием!

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

Используйте правильный инструмент для работы, и вы получите лучшие результаты.

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

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

Удачного кодирования!

Еще подобные статьи:





Я использую эту альтернативу, чтобы «переключиться, чтобы сделать Javascript в 10 раз лучше
Сделайте свой код Javascript более гибким, читабельным и удобным в сопровождении с помощью этой простой альтернативы переключиться.medium.com»