javascript

JavaScript Clean Code: руководство для начинающих разработчиков

  • вторник, 10 сентября 2024 г. в 00:00:02
https://habr.com/ru/companies/otus/articles/841866/

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

Это руководство посвящено написанию чистого и удобного кода на JavaScript и TypeScript. Мы рассмотрим лучшие практики работы с функциями, переменными и методами, чтобы ваш код был не только эффективным, но и понятным как вам, так и другим разработчикам. Статья будет полезна для новичков, которые хотят улучшить свои навыки кодирования.

Имена переменных

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

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

Давайте рассмотрим эту концепцию на примере JavaScript и TypeScript:

// Плохой подход
var c;
var d;
var cd;
var u;

// Хороший подход
let counter = 0;
let date = null;
let currentDate = null;
let user = "";

Булевы (или логические) переменные

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

Вот пример того, как писать чистые и понятные логические переменные на TypeScript:

var v=false;
var w=false;
var c=false;
var l=false;

// Наилучший подход в данном случае
let isValid: boolean = false;
let isWritable: boolean = false;
let isCleared: boolean = false;
let isLoading: boolean = false;

Числовые переменные

При работе с числовыми переменными в коде JavaScript и TypeScript важно использовать осмысленные и описательные имена переменных. Эта практика улучшает читаемость и сопровождение кода, облегчая вам и вашей команде понимание, почему назначены эти переменные. Вместо использования вызывающих вопросы имён, таких как bMax, bMin и bTot, выбирайте имена, которые чётко передают их смысл. Кроме того, используйте систему типов TypeScript для обеспечения типобезопасности — это помогает выявлять потенциальные ошибки на этапе разработки.

Ниже улучшенный пример на TypeScript:

var bMax=false;
var bMin=false;
var bTot=false;

// Наилучший подход в данном случае
let booksMaximum: number = 50;
let booksMinimum: number = 1;
let booksTotal: number = 51;

Имена массивов 

При работе с массивами или списками в JavaScript и TypeScript также важно выбирать осмысленные и описательные имена для ваших переменных. Используйте стиль написания camelCase для наименования и давайте понятные имена, которые указывают на назначение массива. Вот как можно улучшить код:

// Плохой подход к именованию и отсутствие информации о типе
var f = ["Mango", "Papaya", "Apple", "Banana"];
var c = ["Mazda", "Toyota", "Nissan", "Ford"];
var m = ["Fish", "Pork", "Beef", "Chicken"];

// Наилучший подход к случае массивов или списков 
let fruits: string[] = ["Mango", "Papaya", "Apple", "Banana"];
let carBrands: string[] = ["Mazda", "Toyota", "Nissan", "Ford"];
let meatsType: string[] = ["Fish", "Pork", "Beef", "Chicken"];

Функции должны быть короткими и целенаправленными 

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

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

До рефакторинга (JavaScript):

function calculateOrderTotal(order) {
  let total = 0;
  for (const item of order.items) {
    total += item.price * item.quantity;
  }

  if (order.promoCode) {
    total -= total * 0.1;
  }

  if (total > 1000) {
    total -= 100;
  }

  return total;
}

После рефакторинга (TypeScript):

interface Order {
  items: Array<{ price: number; quantity: number }>;
  promoCode?: string;
}

function calculateOrderTotal(order: Order): number {
  const subtotal = calculateSubtotal(order.items);
  const discountedTotal = applyPromoCode(subtotal, order.promoCode);
  return applyDiscountIfApplicable(discountedTotal);
}

function calculateSubtotal(items: Array<{ price: number; quantity: number }>): number {
  return items.reduce((acc, item) => acc + item.price * item.quantity, 0);
}

function applyPromoCode(subtotal: number, promoCode?: string): number {
  if (promoCode) {
    return subtotal - subtotal * 0.1;
  }
  return subtotal;
}

function applyDiscountIfApplicable(total: number): number {
  if (total > 1000) {
    return total - 100;
  }
  return total;
}

Не забывайте комментировать код

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

Вот пример на TypeScript, иллюстрирующий использование комментариев:

// Определение пользовательского типа для структуры объектов массива
type MyObject = {
  id: number;
  name: string;
};

/**
 * Сортируеем массив MyObject по свойству 'id' в порядке возрастания.
 *
 * @param array - Массив, который необходимо отсортировать.
 * @returns Новый массив с объектами, отсортированными по 'id'.
 */
function sortObjectsById(array: MyObject[]): MyObject[] {
  return array.slice().sort((a, b) => a.id - b.id);
}

const arrDevelopers = [
  { id: 3, name: "Jorgel", language: "PHP" },
  { id: 0, name: "Eleazar", language: "Python" },
  { id: 1, name: "Hector", language: "Java" },
  { id: 2, name: "Alvison", language: "Golang" },
  { id: 4, name: "Bill", language: "JavaScript" },
];

sortObjectsById(arrDevelopers)

Используйте пробелы

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

function calculateTotalPrice(items: Item[]): number {
    let totalPrice = 0;

    for (const item of items) {
        totalPrice += item.price * item.quantity;
    }

    return totalPrice;
}

class Item {
    constructor(public name: string, public price: number, public quantity: number) {}
}

const shoppingCart: Item[] = [
    new Item("Laptop", 1000, 2),
    new Item("Mouse", 30, 3),
    new Item("Keyboard", 50, 1),
];

const total = calculateTotalPrice(shoppingCart);

console.log(`Total price: $${total}`);

Избегайте использования вложенных условных операторов

Используйте шаблоны раннего возврата или защитные условия (guard clauses), для улучшения читаемости кода и снижения его сложности. Вместо множества вложенных операторов if-else создавайте функции или методы, которые возвращают результат, как только выполняется условие. Такой подход улучшает организацию кода и упрощает его понимание для разработчиков.

Вот пример на TypeScript, демонстрирующий использование ранних возвратов:

function calculatePrice(quantity: number, price: number, discountApplied: boolean): number {
    if (quantity <= 0) {
        return 0; // Защитное условие для обработки невалидного ввода
    }

    let total = quantity * price;

    if (discountApplied) {
        total *= 0.9; // Применение скидки 10% 
    }

    return total;
}

Избегайте глобальных переменных 

Один из ключевых принципов написания чистого и поддерживаемого кода на JavaScript и TypeScript — это уменьшение использования глобальных переменных. Злоупотребление глобальными переменными может привести к таким нежелательным последствиям, как конфликты имён, непредвиденные побочные эффекты и усложнение кода. Чтобы избежать этих проблем, используйте замыкания или модульные паттерны для инкапсуляции кода и предотвращения загрязнения глобального пространства имён.

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

// Вместо глобальных переменных инкапсулируйте свой код в модуль
namespace MyModule {
  // Приватная переменная, доступная только в пределах модуля
  let counter = 0;

  // Публичная функция для взаимодействия с модулем
  export function incrementCounter(): void {
    counter++;
  }

  // Публичная функция для получения значения счетчика
  export function getCounterValue(): number {
    return counter;
  }
}

// Использование функций модуля
MyModule.incrementCounter();
console.log(MyModule.getCounterValue()); // Вывод: 1

Используйте const и let вместо var 

При объявлении переменных в JavaScript или TypeScript лучше всего использовать const и let, а не var. Этот выбор улучшает читаемость кода, обеспечивает блочную структуру и снижает риск случайного переназначения переменных. Использование const для значений, которые не будут меняться, и let для переменных, которые нужно переназначить, поможет создавать более чистый и предсказуемый код. Вот пример на языке TypeScript:

// Использование const для констант
const pi = 3.14;
const appName = "MyApp";

// Использование let для переменных
let counter = 0;
let username = "JohnDoe";

// Попытка переназначить константу приведет к ошибке компиляции
// pi = 3.14159; // Error: Cannot assign to 'pi' because it is a constant.

// Переназначение переменной вполне допустимо
counter = 1;
username = "JaneDoe";

Используйте шаблонные строки

Одной из важных практик написания чистого и поддерживаемого кода в JavaScript и TypeScript является использование шаблонных строк для интерполяции строк. Шаблонные строки обеспечивают краткий и удобный способ встраивания переменных и выражений в строки, избавляя от необходимости конкатенации или сложных манипуляций со строками. Шаблонные строки заключаются в обратные кавычки и могут содержать заполнители для переменных или выражений, заключенных в «${}»

Вот пример на EcmaScript:

// Определение имени и фамилии
const firstName = "John";
const lastName = "Doe";

// Создание полного имени с использованием шаблонного литерала
const fullName = `My name is ${firstName} ${lastName}.`;

// Вывод полного имени в консоль
console.log(fullName);

Используйте стрелочные функции

При работе с простыми функциями обратного вызова (коллбэками) в JavaScript и TypeScript рекомендуется использовать стрелочные функции. Стрелочные функции предоставляют более лаконичный и удобочитаемый синтаксис по сравнению с традиционными функциональными выражениями. Они автоматически привязываются к окружающему контексту, что помогает уменьшить многословие кода. Вот пример на TypeScript, иллюстрирующий это:

// Традиционное функциональное выражение
const add = function (a: number, b: number): number {
  return a + b;
};

// Стрелочная функция для той же задачи
const add = (a: number, b: number): number => a + b;

Используйте деструктуризацию объектов

В JavaScript и TypeScript чистота и поддерживаемость кода во многом зависят от деструктуризации объектов. Этот приём упрощает доступ к свойствам, улучшает читаемость кода и снижает его избыточность, особенно при работе с вложенными объектами. Вместо индивидуального обращения к каждому свойству, деструктуризация позволяет извлекать необходимые свойства, что способствует более чистому и интуитивно понятному коду.

Пример на TypeScript, иллюстрирующий эту концепцию:

// Пример пользовательского объекта
const user = {
  id: 1,
  name: 'John Doe',
  email: 'john@example.com',
  address: {
    street: '123 Main St',
    city: 'Anytown',
    zipCode: '12345',
  },
};

// Без деструктуризации
const userName = user.name;
const userCity = user.address.city;

// С деструктуризацией
const { name, address: { city } } = user;

console.log(`User Name: ${name}`);
console.log(`User City: ${city}`);

Имена функций 

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

function calculateTotalPrice(items: Item[]): number {
  // Логика расчета общей цены
}

Бонус: оптимизируйте свой код, используя режим EcmaScript и стрелочные функции. Вот несколько примеров:

const paymentUpdatedMessage = 'Payment has been updated!';
function payment(){
console.log(paymentUpdatedMessage);
}


// Лучший подход при использовании обычного JS и ES6
function updatePayment(){
console.log(paymentUpdatedMessage);
}

// Лучший подход с использованием EcmaScript
const updatePayment = () =>
console.log(paymentUpdatedMessage);
}

Заключение

В этом руководстве по JavaScript и TypeScript мы рассмотрели лучшие практики для того, чтобы ваш код оставался эффективным, масштабируемым и подходящим для командной работы. Постоянное применение таких принципов, как DRY, KISS, SOLID, а также фокус на осмысленных названиях и документации повысят качество вашего кода и сэкономит время, делая вас ценным членом команды.


Всех тех, кто использует или планирует использовать JavaScript для написания автотестов, приглашаем на открытый урок, посвященный оптимизации автоматизации тестирования с применением Page Object в JavaScript.

На этом занятии мы погрузимся в мир Page Object и рассмотрим, как эта популярная практика может значительно улучшить процесс автоматизации тестирования в JavaScript. Мы изучим основные концепции Page Object, принципы его использования, а также рассмотрим методы оптимизации автоматизированных тестов с помощью Page Object.

Записаться на урок можно по ссылке.