JavaScript Clean Code: руководство для начинающих разработчиков
- вторник, 10 сентября 2024 г. в 00:00:02
Мы часто сталкиваемся с тем, что многочисленные ресурсы и руководства по 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
При объявлении переменных в 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.
Записаться на урок можно по ссылке.