Много хора ме питат - „Хей, значи правих малко javascript и е едновременно ужасяващо и прекрасно; Чувствам се повдигнат и вдъхновен от неограничените възможности, но също и уплашен от задушаващия натиск да бъда изправен пред безкрайно множество възможности за избор. Понякога имам чувството, че се лутам безцелно в пясъчника, без да знам дали съм на прав път. Как да разбера дали правя това правилно? Как изглежда добрият код?“

Страхотен въпрос! Като човек, който никога не е ходил на компютърно училище, от известно време се опитвам да разбера това. И въпреки че вероятно все още нямам перфектен отговор, мога поне да споделя някои от прозренията, които събрах в хода на моето травматично и обогатяващо пътуване, изучавайки javascript в интернет.

Сигурен съм, че бихте могли да намерите по-подходящо обяснение от професионалист или академик, или просто като цяло от по-разбираем медиен писател, но това, което мога поне да ви предложа тук, са три основни ръководни принципа, които ми се искаше да знам, когато бях още повече като напълно начинаещ.

1. ДОБРИЯТ КОД РЕШАВА ПРОБЛЕМА

„Е, това е доста очевидно, нямаше да е добър код, ако не работеше.“

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

„Но какво ще стане, ако потребителят на stackoverflow, от който копирах, е написал наистина добър код? Това не означава ли, че все още е добър код, независимо дали го разбирам или не?“

За да не изпадам във философия, но едно от основните неща, които направиха кода добър, беше, че той беше резултат от честни усилия от страна на автора, който добросъвестно го пусна безплатно в интернет, така че по-малко опитните потребителите могат да се поучат от него; ако грабвате кода, без да желаете да участвате в този процес на обучение, вие оставяте важна част от това, което го е направило добър.

„Хайде, наистина не е нужно да разбирам всички малки подробности. Някой друг вече е написал кода и той работи добре за това, което трябва да направя.“

Хей, не казвам, че винаги трябва да пишете добъркод, ако имате нужда само да бъде достатъчно добър. Това често е напълно валидно (например, ако просто изграждате прототип или ако не изграждате нещо наистина сериозно), но ако искате да сте подготвени да се справите с неочакван резултат, да коригирате грешка в движение или да разширите функционалността на програмата, вероятно трябва да има поне някаква представа за това как работи този код.

В края на краищата част от решаването на проблема е да се гарантира, че той няма да остане нерешен.

2. ДОБРИЯТ КОД Е ЕФЕКТИВЕН

„Но как всъщност можете да разберете дали е ефективно?“

Сигурен съм, че бихме могли да напишем цяла друга статия само за това какво означава кодът да бъде ефективен, но засега няма да навлизаме в нюансите на времева/пространствена сложност, нотация с голямо О, или нещо подобно че. Нека просто започнем с един основен принцип...

За да илюстрираме концепцията на основно ниво, да кажем, че се опитваме да напишем функция, която приема цяло число n и връща сумата от всички числа от 1 до n

Алгоритъм 1:

function sumFromOne(n) {
    let numbers = [];
    for(let i = 1; i <= n; i++) {
        numbers.push(i);
    }
    let sum = numbers.reduce((a, b) => a + b);
    return sum;
}

Изглежда, че това ще свърши работата, но наистина ли трябва да съхраняваме този масив в паметта? Вече преминаваме през цикъл от 1 до n, така че наистина ли има смисъл да обхождаме отново всички тези елементи на масива с reduce? Не карайте кода си да прави безполезни неща.

Алгоритъм 2:

function sumFromOne(n) {
    let sum = 0;
    for(let i = 1; i <= n; i++) {
        sum += i;
    }
    return sum;
}

Хей, сега е много по-добре; можем просто да съхраним сумата в една променлива от самото начало. Но наистина ли е необходим този цикъл? Не карайте кода си да прави безполезни неща!

Алгоритъм 3:

function sumFromOne(n) {
    return n * (n + 1) / 2;
}

Е, сега изглежда, че сме достигнали алгоритъм с постоянно време, свещеният граал на алгоритмичната сложност. Независимо колко голям е входът n, този последен алгоритъм винаги ще прави само едно изчисление, така че винаги трябва да отнема едно и също време и няма да съхранява никакви допълнителни неща в паметта. Нищо безполезно в това! Не можем да кажем същото за първите два опита, така че алгоритъм 3 е обективно по-ефективен.

„Добре, разбирам, не правете безполезни неща. Но как всъщност мога да разбера кои части от кода са безполезни?“

Е, това може да бъде доста трудно; няма магическо правило за това и наистина зависи от ситуацията, но ето някои евристики, които обикновено намирам за полезни:

  • Опитайте се да преминете през масив само веднъж, ако можете, или още по-добре избягвайте изобщо използването на масив, ако данните, от които се нуждаете, в крайна сметка могат да бъдат обобщени в една променлива
  • Развийте естествена алергия към вложени цикли, тъй като те могат моментално да направят кода по-сложен. Понякога няма друг вариант, но това трябва да е нещо като последна инстанция
  • Следвайте принципа СУХО. Използвайте променливи, за да съхранявате всички стойности, които ще трябва да използвате повече от веднъж; напишете функция за всички процеси, които ще трябва да извършите отново, и използвайте техники като динамично програмиране или запаметяване, за да избегнете повторни изчисления
  • Понякога сложността може да се крие в сенките; например използването на map или reduce трябва да премине през целия масив, така че трябва да свърши почти същото количество работа като цикъл. Същото с indexOf или includes и много други вградени, така че бъдете внимателни, когато извиквате тези методи
  • Внимавайте за неща като slice, които ще „копират дълбоко“ масив (ефективно удвоявайки изискванията за памет). Вероятно бихте могли да спестите място, като вместо това промените масива на място
  • Не забравяйте, че не всички еднакво сложни алгоритми са еднакви; дори ако сложността не може да се подобри, може да сте в състояние да извършите микрооптимизации, за да подобрите времето за изпълнение; например, като използвате ключовата дума break, за да спрете цикъл веднага щом имате всички необходими данни
  • Като цяло, използването на по-специализирани обекти води до намаляване на сложността, тъй като интерпретаторът не трябва да разглежда толкова много възможности; например, с „типизиран масив“, не е нужно да се тревожим, че масивът съдържа други типове данни или променя размера си. Като използваме const вместо var, не е нужно да се тревожим за възможността променливата да бъде преназначена
  • Освен ако не е напълно нов и неясен тип проблем, някой вероятно вече го е проучил и е разработил някои оптимални стратегии. Така че вероятно си струва да сте запознати с обичайните алгоритъмни техники (напр.: търсене, сортиране) и структури от данни (напр.: хеш карти, дървета), вместо да разчитате само на техники за груба сила

По принцип просто бъдете умни за това - търсете математически модели, проверете за начини за преки пътища и не се задоволявайте с алгоритъм само защото той върши работата.

И имайте предвид, това е постепенно; не е необходимо да стане перфектно веднага, но като правите малки подобрения едно по едно, в крайна сметка ще стигнете до там.

3. ДОБРИЯТ КОД ИЗГЛЕЖДА ХУБАВО

„Чакай какво? Защо това има значение? Докато приложението/играта работи гладко без грешки, на кого му пука как изглежда кодът зад него?“

Е, това не е ли като да кажеш „на кого му пука какви са съставките, стига храната да е вкусна“? Това, което е вътре, е важно.

В по-практичен смисъл, има голям шанс вашият код да бъде преразгледан в някакъв момент в бъдеще (напр.: за разширяване на функционалността, коригиране на грешка, прилагане на незначителна актуализация, разклонение в нов проект) и може да бъде далеч в бъдещето, далеч отвъд момента, в който ще си спомняте нещо за него. Казват, че трябва да кодирате за следващия разработчик и този следващ разработчик може много добре да бъде вашето бъдещо аз, така че вашият код основно трябва да бъде собствено ръководство с инструкции.

„Но по-краткият код заема по-малко място и затова ще работи по-бързо, нали? Така че не трябва ли винаги да използваме най-кратките имена на променливи и да тъпчем всичко заедно?“

Това може да е крайна позиция, но бих приел някои незначителни компромиси в производителността, ако това означава, че кодът ще бъде значително по-лесен за разбиране от следващия разработчик. В противен случай времето, което ще спестите, като накарате приложението да работи малко по-бързо, ще бъде по-малко от времето, което ще загубите в опити да разберете какво, по дяволите, става, докато се опитвате да разплетете голяма купчина спагети.

Да не говорим, че с днешните инструменти за „минимизиране“ можете да правите имената на променливите и функциите си толкова дълги, колкото пожелаете, и това няма да повлияе на крайния внедрен продукт, така че това не е наистина проблем

„Добре, какво конкретно трябва да направя, за да направя кода да изглежда добре?“

Отново, тук има известна субективност, но мога да ви дам няколко бързи правила:

  • Бъдете последователни, но адаптивни. За много от нещата, които бихте искали да правите в програмирането, можете да намерите универсално предпочитана стратегия, но понякога всичко се свежда до личен избор; като например дали да използвате раздели срещу интервали, копиране на масив с помощта на array.slice() срещу […array], използване на функции със стрелки срещу традиционни декларации на функции и т.н. Всяко от тези е добър избор, но вероятно е добра идея да бъдете последователни за него
  • Дайте на кода си малко място за дишане. Използването на интервали и добавянето на нови редове за разделяне на кода на по-управляеми блокове ще го направи много по-лесен за разглеждане и по този начин по-лесен за разбиране и отстраняване на грешки
  • Изберете мъдро имената на вашите променливи и функции; нещо достатъчно описателно, за да е ясно на читателя какво представлява, но вероятно не толкова дълго, че да започне да претрупва кода. От вас зависи дали да използвате „случай с камила“, „случай със змия“ или каквото и да е друго, но се опитайте да бъдете последователни в това
  • Вероятно опитайте редовете да не продължават твърде дълго (стандартната конвенция е да използвате ограничение от 80 знака). В javascript можете безопасно да вмъкнете нов ред почти навсякъде, където бихте използвали интервал, така че не е много трудно да го направите подходящ
  • Винаги използвайте подходящ отстъп (2 интервала или 4 интервала; няма значение, стига да сте последователни). И ако установите, че наистина ви е трудно с това, можете да опитате да кодирате в Python за няколко седмици
  • Определено използвайте коментари, но постигнете правилния баланс – няма да е необходимо да включвате толкова много коментари, ако кодът ви говори сам за себе си, така че имайте това предвид, когато избирате имената на функциите и променливите. Това може да се отнася дори за вашия избор на методи; например array.includes(element) вероятно е по-ясно за читателя от array.indexOf(element) > -1

„Всичко това звучи като много усилия...“

Абсолютно! Точно както в говоримия език, винаги са необходими време и усилия, търпение и разбиране, за да се разберете ясно и това е само в полза на получателя. Яснотата е благотворителност, но често вие ще бъдете благодетелят, тъй като със сигурност ще преразгледате кода си в някакъв момент в бъдещето. Така че може и да улесните себе си :)

Така че по принцип, ако искате да пишете добър код, трябва да го накарате да работи, да го направите ефективен и да изглежда добре. Имайте предвид, че тези три качества понякога могат да бъдат в противоречие едно с друго; ще трябва да намерите правилния баланс между разширяемост и оптимизация, между краткост и четливост. Но намирайки този баланс, намирайки стила си, вие наистина можете да направите нещо красиво.

И само да ви напомня, че това не е окончателен списък или крайно ръководство; това са само някои скъпоценни камъни на мъдростта, които събрах в хода на това просветляващо и ужасяващо пътуване в javascript. Но хей, пътуването продължава, така че ако имате някакви други бомби със знания за споделяне, не се колебайте да ги пуснете в коментарите по-долу!