Чытальны JavaScript па прыкладах: Дзесяць перэўтварэнняя да і пасля
Дзесять невяліках перерабоцанняў коду на JavaScript і React: ад называння і об’ектаў параметраў да карыстоўвання механізмаў обробкі памылак і форматавання, якія спрабуюць упрыгожыць код для лёгкага чытання і змены ад другога разработчыка.
Крыніцы: 10 Clean Code Principles I Wish I Knew When I Started Coding
З’явленне функцыяналу — гэта першая частка работы. Другая частка — абяцкаліце, каб колега па командзе, або вы самі за шасць месцаў, моглі прачытаць гэты код, безпечна яго змяніць і не пераканацца ў його значэнні. У гэтым артыкуле рассказваецца пра дзесять конкрэтных прыкладаў переработак у JavaScript і React, кожны з якіх супаўнечаны абґрунтаванням і пунктом, дзе ці гады перестаюць быць корыстнымі.
1. Называйце значэнні па таму, што яны представляюць
Короткія, загадковыя назвы змушаюць чытальніка шукать інфармацыю ў іншых месцах. У гэтым фрагменте нічога не паведамляе, што ёсць d і якая властасць знаходзіцца у a.
const d = 30;
const x = users.filter(u => u.a);
Перэназыванне обох элементаў робіць намеры ўжывання коду відразу виднымі. Чысла стаюць пазначэнням трываласці тэсту:
const trialPeriodDays = 30;
А фільтр тепер выглядае як рэченне пра актывах корыстніках:
const activeUsers = users.filter(user => user.isActive);
Хораша назва паводзіць зразуме, што представляе сабе значэнне; узагальненыя назвы, такія як data, практычна нічога не паводзяць.
const data = getData();
Назва, яка выказвае канцэпцыю домэна, ўсё больш інформатывальная:
const customerOrders = getCustomerOrders();
Якщо вы відчуваеце патрэбу дадаць каментар, які пояснюе зменную, гэта зазвычай ўскладненне, што сама назва павінна несці гэю інфармацыю.
2. Даўкож функцыі адну задачу
Функцыя, якая пераверчае вхідны данні, прабоўвае іх, запісвае ў базу дадзенаў, адправляе электрана і логуе дзейнасць, ёй важка следаваць і яшчэ важка теставаць, адтуды кожны тэст павінен адразу ўрахоўваць усе гэтыя аспекты. Нижэй паказана структура такой функцыі з кожной адпаведнуюю задачай.
function createUser(user) {
// validate user
// format data
// save to database
// send email
// log activity
}
Разбіўка яе на адзінакавыя крокі ператварае функцыю вышэйшага рангу на чытабельны падсумак. Яна пачынаецца з пераверкі:
function createUser(user) {
validateUser(user);
І далей ідзе форматаванне, зберагчыць і ласкава электрана, кожна з яых прадаўана сваймі функцыяй:
const formattedUser = formatUser(user); saveUser(formattedUser); sendWelcomeEmail(formattedUser);
}
Тепер createUser() выглядае як спіс крокаў, і кожную дапаможную функцыю можна тэставаць адсалідно. Але ёсць недзея — чытальнік, якому трэбаюць деталі, должен заходзіць у кожную дапаможную функцыю, таму імены яных должны быць настолькі точнымі, каб гэта рэдкасць.
3. Адзёрваць дублікацыю, якая мае аднаковую прычыну для змены
Павтараючыся логіка ўзьмлівае для выяўлення. Хутчэй тут тая ж формула налога павтарыцца трохі разоў.
const total1 = price1 + price1 * 0.15;
const total2 = price2 + price2 * 0.15;
const total3 = price3 + price3 * 0.15;
Перакладзенне яе ў функцыю дае правілу імя і аднаковае месца знаходжэння:
function calculatePriceWithTax(price) {
return price + price * 0.15;
}
Кожны месца вызову проста застосоўвае гэта правіло:
const total1 = calculatePriceWithTax(price1);
const total2 = calculatePriceWithTax(price2);
const total3 = calculatePriceWithTax(price3);
Якщо стаўка зменшыцца з 15% да 18%, даскладзецца змяніць толькі адна лінія, у працоўным проекте не патрэбна дакладная пераследжванне. Важным умовай є адзьменаванне справжняго дуплікацыі, а не кожных пар ліній, якія толькі выглядаюць аднакова. Две часткі коду, якія сьогодні ідэнтычны, але змянююцца з разных прычын, краща залишыць окремымі; ўзаўмесяднаванне іх спаявае тое, што павінна евалюваць незалежна. Статыя на тэму таго, калі павінен існаваць спадзяльны код дакладна розглядае гэты компроміс.
4. Заменіце дугія спискі позыцыйных параметраў на об’ект
Позыцыйныя аргументы закрываюць свой значэнне ў месцы вызыву. Паглядзевшы на гэты вызыв, немагчыма зрозумець, што означаюць 25 чы true, не ачынуўшы вялікання функцыі.
createUser(
"Shayan",
"shayan@example.com",
25,
"developer",
true,
"Pakistan"
);
Працэўка з адним об’ектам, які мае іменаваныя атрыбуты, робіць кожную значэнне самааспрошаным:
createUser({
name: "Shayan",
email: "shayan@example.com",
age: 25,
role: "developer",
isActive: true,
country: "Pakistan"
});
Гэты стыль вже ўжо є стандартам у пакетах дадзеных API, об’ектах налаштавання і пропсах React, таму ён здаецца прыродным у большых праектах.
5. Валіце зрозумелы код пра розумны код
Складванне логіки ў адну лінію можа ускладніць яе чытанне. У прыведзенай нижэй лініі выкананы неабязковыя вызовы через скраценыя імена атрыбутаў.
const result = users?.filter(u => u.a)?.map(u => u.n);
Раздзелэнне ціяго процесу на два крокі з описавымі іменамі робіць кожную трансформацыю явной. Спачатку выбіраюцца актыўныя корыстувальнікі:
const activeUsers = users.filter(user => user.isActive);
Потым выкалываюцца ўсі іх імены:
const activeUserNames = activeUsers.map(user => user.name);
Гэта выклікае дадзенне адзінай дапаможнай лініі, але чытальнік можа зразу ўсё зрозумець. Заўважыце аднае разлічнае падходжэнне: у первасным варыянце была выкарыстоўвана необавязковая ланцюговая аперацыя, таму ён перажываў, калі users быў null або undefined, тады калі ж у перапісаным варыянце такі случай вызывае адхэленне. Якщо users можа законна не існаваць, трэба явна з розумем цяпероўшчыну (напрыклад, за дапамогою значэння [] як стандарту), а не хаваць гэта ў ланцюгу ?.. Кансэпт заключаецца у тым, каб код быў зрозумілы, а не вражаючы.
6. Планаваць не толькі успех, але і невыпадковасці
Рэальныя системы зламваюцца: API вяртае адказы пра адхэленні, корыстувальцы надаюць невярныя данні, сеткі з’яўляюцься недоступнымі, базы дадзеных выключаюцца. Наступны код прыпускае, што всё будзе работаць без проблем.
const response = await fetch("/api/users");
const users = await response.json();
Ключовым моментам ёсць тое, што fetch адхоўваеся толькі у разе неудач на сетцы; станы HTTP 404 або 500 все равно працуюць. У пасляўзняй версіи запыт абгорнуты ў try:
try {
const response = await fetch("/api/users");
потым пераканальваецца response.ok, выклікаецца адхоўк у разе неудачнага стану, параскладваецца тэла і ловіцца будзь-што, што пайшло не так:
if (!response.ok) {
throw new Error("Failed to fetch users");
} const users = await response.json(); return users;
} catch (error) {
console.error("Unable to load users:", error);
}
Эта прычына робіць неудачы виднымі і значна спрыяе ў дэбагаванні. Зважайце, што блок catch тут фіксуе проблему, а потым неявна вяртае undefined, таму вызываючыя коды не можуць адразніць неудачу ад порожняга рэзультата. У рэальным коде трэба альбо зноў выклікаць адхоўку пасля фіксацыі, альбо вяртаць явныя значэння, якія можа пераканальваць вызываючы код, так што адхоўка будзе адмаўлена там, дзе яе можна фактычна адмаўліць.
7. Каментаваць «чырэй», а не «што»
Каментары ўжыткавыя; зайвыя — галас. Штырый каментар проста павтарае тое, што робіць цяперашняя лінія.
// Increment counter by 1
counter++;
Хорашыя прыемкі адказваюць на запыткі, якія код сам па сабе не можа выразыць, напрыклад, чаму запыт навмесна затрымліваецца:
// Delay the request to avoid triggering the API rate limit.
setTimeout(fetchUsers, 1000);
Практычны правіла: нехай код сам паскажа, што ён робіць, а прыемкі нехай адказваюць на пытанне, чаму ён гэта робіць.
8. Не включаць бізнес-правілы ў JSX
У компонентах React лёгкая ўместіць фільтрацыю і вычысленні працэсоў безпосередна ў маркап. У гэтым выходе дадзены два фільтра пры самай рэндарызацыі, перш чым выконваецца обработка тавароў.
<div>
{products
.filter(product => product.stock > 0)
.filter(product => product.price < 100)
.map(product => (
<ProductCard product={product} />
))}
</div>
Пераклад правіла ў зменную з іменам раздзеляе тое, што лічыцца як доступная тавар, ад способу ўтворэння візуальнага адображэння тавароў:
const availableProducts = products.filter(
product => product.stock > 0 && product.price < 100
);
Тады JSX описвае толькі інтерфейс:
<div>
{availableProducts.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
Заўважыце, што переработаная версія таксама дадае атрыбут key, якога не было ў первінной версіі, і які неабходны React для правильнага стэрагування элементамі спіса.
9. Нека інструменты забезпечваюць адносны форматаванне
Форматаванне здаецца незначным, пакуль база коду не сумешчае розныя стылі. У гэтым прыкладзе тая жа зменная адгласавана трохма способамі разнымі аднародзямі: camelCase,
const userName = "Shayan";
snake_case,
const user_name = "Shayan";
і PascalCase без прасоўкі чы рэзкама:
const UserName="Shayan"
Усе тры варыянты працуюць, але такая нэадноснасць стварае труднасці ўсякі раз, калі хтось чытае чы шукае код. Інструменты такія як Prettier і ESLint усунуць гэтыя супераспыткі: Prettier автоматычна форматуе код, а ESLint можа прымусваць стандарты называння і паказваць распашчытныя помылкі.
10. Чырайце так, каб гэты код мог утримваць следующы чалавек
Гэты прынцып спаявае ўсі іншыя. Наступны чалавек, які ачыне ваш файл, можа быть новым колегай, саавтарам чы вашым будучым я, а ваша будучая версія забудзе все, пра што вы думалі сёньдзе.
Перш чым зберагаць код, запытайце ся, чы розумелі бы вы файл, якбы адкрылі яго праз шасць месца. Якщо няма, спростаце яго: перайменаваце неясныя зменныкі, раздзеляйце вялікія функцыі, адмахніцеся від дублікацый, якія маюць аднаковую прычыну для змены, і падкрэпіце структуру. Асновная мета — зменшыць колькасць разумовых зусібоў, неабходных для розумення коду. Чырвоныя прывычкі ў там самым духу можна пазнакоміцца за лінкам «Прывычкі, якія спрыяюць таму, каб наступны разработчык легка мог зменіць код».
Заключанне
Чысты код не значыць ідеальны код. Канечныя даты, змены ў ваказаннях і развіцце проектаў значыць, што кожная команда пішае часткова неразбірлівы код. Важнае — напрамак: кожная змена павінна робіць код чырвеней, простейшым і лёгкім для падтрымкі.
- Назвы павінны паказваць, што представляе значэнне.