Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

🛁 Clean Code concepts adapted for JavaScript (Polish) 🇵🇱 Czysty kod JavaScript, polskie tłumaczenie

License

NotificationsYou must be signed in to change notification settings

greg-dev/clean-code-javascript-pl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 

Repository files navigation

Original Repository:ryanmcdermott/clean-code-javascript

Czysty kod JavaScript

Spis treści

  1. Wprowadzenie
  2. Zmienne
  3. Funkcje
  4. Obiekty i struktury danych
  5. Klasy
  6. SOLID
  7. Testowanie
  8. Współbieżność
  9. Obsługa błędów
  10. Formatowanie
  11. Komentarze
  12. Tłumaczenie

Wprowadzenie

Humorystyczny obrazek przedstawiający ocenę jakości oprogramowania za pomocą ilości przekleństw wykrzyczanych podczas czytania kodu

Zasady inżynierii oprogramowania z książki Roberta C. MartinaCzysty kod,dostosowane do języka JavaScript. Nie są to wytyczne dotyczące stylu. To wytyczne do tworzeniaczytelnego, prostego w refaktoryzacji i wielokrotnym użyciuoprogramowania w języku JavaScript.

Nie każda z podanych tu zasad musi być ściśle przestrzegana i nie wszystkie z nich będąpowszechnie przyjęte. To nic więcej, niż wskazówki zebrane dzięki wieloletniemudoświadczeniu autorówCzystego kodu.

Inżynieria oprogramowania ma trochę ponad 50 lat i nadal wiele się uczymy.Gdy architektura oprogramowania będzie tak stara, jak sama architektura,wtedy może będziemy mieli trudniejsze zasady do przestrzegania. Na razie niech tewytyczne służą jako podstawa do oceny jakości kodu JavaScript, który Ty i Twój zespół tworzycie.

Jeszcze jedno: poznanie zasad nie zrobi z Ciebie lepszego programisty w mgnieniu oka,a wieloletnia praca zgodnie z nimi nie sprawi, że przestaniesz popełniać błędy.Każdy kawałek kodu zaczyna się od wstępnego szkicu i jest jak mokra glina formowanado ostatecznego kształtu. Wreszcie niczym rzeźbiarz dłutem usuwamy wszelkie niedoskonałościpodczas przeglądu kodu wspólnie z kolegami. Nie zadręczaj się wstępnymi szkicami wymagającymi poprawek.Zamiast tego męcz kod!

Zmienne

Używaj znaczących i wymawialnych nazw zmiennych

Źle:

constyyyymmdstr=moment().format('YYYY/MM/DD');

Dobrze:

constcurrentDate=moment().format('YYYY/MM/DD');

⬆ powrót na początek

Używaj tego samego słownictwa dla tego samego rodzaju danych

Źle:

getUserInfo();getClientData();getCustomerRecord();

Dobrze:

getUser();

⬆ powrót na początek

Używaj odnajdywalnych nazw

Będziemy czytać więcej kodu, niż kiedykolwiek napiszemy. Ważne jest, aby nasz kodbył czytelny i przeszukiwalny.Nie nazywając zmiennych, mających znaczeniedla zrozumienia naszego programu, krzywdzimy czytających.Spraw, by Twoje nazwy były odnajdywalne. Narzędzia takie jakbuddy.js iESLintmogą pomóc zidentyfikować nienazwane stałe.

Źle:

// Czym do diaska jest 86400000?setTimeout(blastOff,86400000);

Dobrze:

// Zadeklaruj ją jako nazwaną stałą, używając wielkich liter.constMILLISECONDS_IN_A_DAY=86400000;setTimeout(blastOff,MILLISECONDS_IN_A_DAY);

⬆ powrót na początek

Używaj zmiennych wyjaśniających

Źle:

constaddress='One Infinite Loop, Cupertino 95014';constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;saveCityZipCode(address.match(cityZipCodeRegex)[1],address.match(cityZipCodeRegex)[2]);

Dobrze:

constaddress='One Infinite Loop, Cupertino 95014';constcityZipCodeRegex=/^[^,\\]+[,\\\s]+(.+?)\s*(\d{5})?$/;const[,city,zipCode]=address.match(cityZipCodeRegex)||[];saveCityZipCode(city,zipCode);

⬆ powrót na początek

Unikaj map mentalnych

Jasne jest lepsze niż niejasne.

Źle:

constlocations=['Austin','New York','San Francisco'];locations.forEach((l)=>{doStuff();doSomeOtherStuff();// ...// ...// ...// Czekaj, czym było `l`?dispatch(l);});

Dobrze:

constlocations=['Austin','New York','San Francisco'];locations.forEach((location)=>{doStuff();doSomeOtherStuff();// ...// ...// ...dispatch(location);});

⬆ powrót na początek

Nie dodawaj niepotrzebnego kontekstu

Jeśli nazwa Twojej klasy/obiektu coś mówi, nie powtarzaj tego w nazwie zmiennej.

Źle:

constCar={carMake:'Honda',carModel:'Accord',carColor:'Blue'};functionpaintCar(car){car.carColor='Red';}

Dobrze:

constCar={make:'Honda',model:'Accord',color:'Blue'};functionpaintCar(car){car.color='Red';}

⬆ powrót na początek

Używaj domyślnych wartości argumentów zamiast wykonań warunkowych lub warunków

Domyślne wartości argumentów są zwykle jaśniejsze niż wykonania warunkowe. Pamiętaj, że jeśliich użyjesz, Twoja funkcja dostarczy domyślne wartości tylko dla argumentów niezdefiniowanych (undefined). Inne "fałszywe" wartości, takie jak'',"",false,null,0 iNaN, nie będą zastąpione wartością domyślną.

Źle:

functioncreateMicrobrewery(name){constbreweryName=name||'Hipster Brew Co.';// ...}

Dobrze:

functioncreateMicrobrewery(breweryName='Hipster Brew Co.'){// ...}

⬆ powrót na początek

Funkcje

Parametry funkcji (najlepiej 2 lub mniej)

Ograniczanie ilości parametrów funkcji jest niezwykle ważne, gdyż czynitestowanie Twojej funkcji prostszym. Mając więcej niż trzy, prowadzisz doeksplozji kombinatorycznej, w której musisz przetestować masę różnych przypadkówosobno z każdym kolejnym parametrem.

Najlepiej, jeśli jest jeden lub dwa parametry, trzy powinny być już w miarę możliwości unikane.Większa ilość powinna być skonsolidowana. Zwykle, gdy maszwięcej niż dwa parametry, Twoja funkcja próbuje zrobić za dużo. W przypadkach,gdy tak nie jest, zazwyczaj obiekt wyższego rzędu będzie wystarczającymparametrem.

Jako że JavaScript pozwala tworzyć obiekty w locie i bez konieczności użycia koduzwiązanego z klasami, zawsze możesz użyć obiektu, gdy czujesz, że potrzebujeszdużo parametrów.

Aby było oczywistym, jakich parametrów oczekuje funkcja, możesz użyć destrukturyzacjiwprowadzonej w wersji ES2015/ES6. Ma to kilka zalet:

  1. Gdy ktoś popatrzy na sygnaturę funkcji, od razu będzie mieć jasność, jakiewłaściwości będą wykorzystywane.
  2. Destrukturyzacja klonuje określone prymitywne wartości argumentów obiektuprzekazywanego do funkcji. Może to pomóc w uniknięciu efektów ubocznych. Uwaga:obiekty i tablice będące wynikiem destrukturyzacji argumentów obiektu NIE będąklonowane.
  3. Lintery mogą ostrzec Cię przed nieużywanymi zmiennymi, co będzie niemożliwebez destrukturyzacji.

Źle:

functioncreateMenu(title,body,buttonText,cancellable){// ...}

Dobrze:

functioncreateMenu({ title, body, buttonText, cancellable}){// ...}createMenu({title:'Foo',body:'Bar',buttonText:'Baz',cancellable:true});

⬆ powrót na początek

Funkcja powinna wykonywać tylko jedno zadanie

Jest to zdecydowanie najważniejsza zasada w inżynierii oprogramowania. Gdy funkcjewykonują więcej niż jedno zadanie, są trudniejsze w kompozycji, testowaniu i zrozumieniu.Jeśli możesz ograniczyć działanie funkcji do tylko jednego zadania, będzie onaprostsza w refaktoryzacji, a Twój kod czytelniejszy. Nawet jeśli nie wyniesieszz tego przewodnika nic więcej poza tym, to i tak będziesz do przodu w stosunku do wielu deweloperów.

Źle:

functionemailClients(clients){clients.forEach((client)=>{constclientRecord=database.lookup(client);if(clientRecord.isActive()){email(client);}});}

Dobrze:

functionemailActiveClients(clients){clients.filter(isActiveClient).forEach(email);}functionisActiveClient(client){constclientRecord=database.lookup(client);returnclientRecord.isActive();}

⬆ powrót na początek

Nazwy funkcji powinny mówić co one robią

Źle:

functionaddToDate(date,month){// ...}constdate=newDate();// Z nazwy funkcji trudno wywnioskować, co jest dodawaneaddToDate(date,1);

Dobrze:

functionaddMonthToDate(month,date){// ...}constdate=newDate();addMonthToDate(1,date);

⬆ powrót na początek

Funkcje powinny być tylko jednym poziomem abstrakcji

Gdy masz więcej niż jeden poziom abstrakcji, Twoja funkcja zwykle robi za dużo.Podzielenie funkcji umożliwi wielokrotne użycie kodu i ułatwi jego testowanie.

Źle:

functionparseBetterJSAlternative(code){constREGEXES=[// ...];conststatements=code.split(' ');consttokens=[];REGEXES.forEach((REGEX)=>{statements.forEach((statement)=>{// ...});});constast=[];tokens.forEach((token)=>{// lex...});ast.forEach((node)=>{// parse...});}

Dobrze:

functiontokenize(code){constREGEXES=[// ...];conststatements=code.split(' ');consttokens=[];REGEXES.forEach((REGEX)=>{statements.forEach((statement)=>{tokens.push(/* ... */);});});returntokens;}functionlexer(tokens){constast=[];tokens.forEach((token)=>{ast.push(/* ... */);});returnast;}functionparseBetterJSAlternative(code){consttokens=tokenize(code);constast=lexer(tokens);ast.forEach((node)=>{// parse...});}

⬆ powrót na początek

Usuń powielony kod

Rób wszystko, co tylko możesz, aby uniknąć powielania kodu. Powielony kod jest zły,gdyż oznacza, że jest więcej niż jedno miejsce do zmodyfikowania, gdy potrzebujesz zmienićtrochę logiki.

Wyobraź sobie, że prowadzisz restaurację i sprawujesz nadzór nad swoimi zapasami:wszystkie pomidory, cebule, czosnek, przyprawy itd. Jeśli masz je spisane w wielu miejscach,to wszystkie z nich muszą zostać uaktualnione, gdy serwujesz daniez pomidorami. Mając jedną listę, do uaktualnienia jest tylko jedno miejsce!

Częstokroć powielasz kod, gdyż musisz rozwiązać dwa lub więcej nieznacznie różnych problemów,mających ze sobą wiele wspólnego, a ich różnice wymuszają na Tobie posiadaniedwóch lub więcej oddzielnych funkcji robiących wiele tego samego. Usunięciepowielonego kodu oznacza utworzenie abstrakcji, mogącej obsłużyć ten zestawróżnych problemów za pomocą tylko jednej funkcji/modułu/klasy.

Poprawne użycie abstrakcji jest istotne, dlatego też powinieneś przestrzegaćzasad SOLID, znajdujących się w rozdzialeKlasy. Zła abstrakcja może byćgorsza, niż powielony kod, bądź więc ostrożny! Jeśli możesz zastosowaćdobrą abstrakcję, zrób to! Nie powtarzaj się, w przeciwnym razie skończyszuaktualniając wiele miejsc za każdym razem, gdy chcesz zmienić jedną rzecz.

Źle:

functionshowDeveloperList(developers){developers.forEach((developer)=>{constexpectedSalary=developer.calculateExpectedSalary();constexperience=developer.getExperience();constgithubLink=developer.getGithubLink();constdata={      expectedSalary,      experience,      githubLink};render(data);});}functionshowManagerList(managers){managers.forEach((manager)=>{constexpectedSalary=manager.calculateExpectedSalary();constexperience=manager.getExperience();constportfolio=manager.getMBAProjects();constdata={      expectedSalary,      experience,      portfolio};render(data);});}

Dobrze:

functionshowEmployeeList(employees){employees.forEach((employee)=>{constexpectedSalary=employee.calculateExpectedSalary();constexperience=employee.getExperience();constdata={      expectedSalary,      experience};switch(employee.type){case'manager':data.portfolio=employee.getMBAProjects();break;case'developer':data.githubLink=employee.getGithubLink();break;}render(data);});}

⬆ powrót na początek

Ustawiaj domyślne obiekty używając Object.assign

Źle:

constmenuConfig={title:null,body:'Bar',buttonText:null,cancellable:true};functioncreateMenu(config){config.title=config.title||'Foo';config.body=config.body||'Bar';config.buttonText=config.buttonText||'Baz';config.cancellable=config.cancellable!==undefined ?config.cancellable :true;}createMenu(menuConfig);

Dobrze:

constmenuConfig={title:'Order',// Użytkownik nie uwzględnił klucza 'body'buttonText:'Send',cancellable:true};functioncreateMenu(config){config=Object.assign({title:'Foo',body:'Bar',buttonText:'Baz',cancellable:true},config);// config ma teraz wartość: {title: "Order", body: "Bar", buttonText: "Send", cancellable: true}// ...}createMenu(menuConfig);

⬆ powrót na początek

Nie używaj flag jako argumentów funkcji

Flagi mówią użytkownikowi, że dana funkcja robi więcej niż jedną rzecz. Funkcje powinny robić jedną rzecz. Rozdziel swoje funkcje, jeśli ich kod podąża innymi ścieżkami zależnie od zmiennej boolowskiej.

Źle:

functioncreateFile(name,temp){if(temp){fs.create(`./temp/${name}`);}else{fs.create(name);}}

Dobrze:

functioncreateFile(name){fs.create(name);}functioncreateTempFile(name){createFile(`./temp/${name}`);}

⬆ powrót na początek

Unikaj skutków ubocznych (część 1)

Funkcja daje skutki uboczne, gdy robi cokolwiek innego niż pobranie jednej wartościi zwrócenie innej lub innych. Skutkiem ubocznym może być zapis do pliku,zmodyfikowanie jakiejś zmiennej globalnej, lub też przypadkowe przelanie wszystkichTwoich pieniędzy nieznajomemu.

Czasem potrzebujesz skutków ubocznych w programie. Jak w poprzednim przykładziemożesz potrzebować zapisu do pliku. Tym, co chcesz zrobić, jest znalezienie jednego miejsca,gdzie go umieścisz. Nie miej kilku funkcji i klas, które zapisują do poszczególnych plików.Miej jedną usługę, która to robi. Jedną i tylko jedną.

Główną kwestią jest unikanie powszechnych pułapek, takich jak dzielenie stanu między obiektamibez jakiejkolwiek struktury, użycie mutowalnych typów danych, które mogą być nadpisane przez cokolwieki nieokreślenie jednego miejsca dającego skutki uboczne. Jeśli możesz to zrobić, będziesz szczęśliwszy,niż zdecydowana większość programistów.

Źle:

// Globalna zmienna po której następuja funkcja, która się do niej odnosi// Jeśli będziemy mieć kolejną funckję używającą tej nazwy, teraz będzie ona tablicą i może spowodować błąd.letname='Ryan McDermott';functionsplitIntoFirstAndLastName(){name=name.split(' ');}splitIntoFirstAndLastName();console.log(name);// ['Ryan', 'McDermott'];

Dobrze:

functionsplitIntoFirstAndLastName(name){returnname.split(' ');}constname='Ryan McDermott';constnewName=splitIntoFirstAndLastName(name);console.log(name);// 'Ryan McDermott';console.log(newName);// ['Ryan', 'McDermott'];

⬆ powrót na początek

Unikaj skutków ubocznych (część 2)

W języku JavaScript typy proste przekazywane są przez wartość, a obiekty/tablice przezreferencję. W przypadku obiektów i tablic, jeśli funkcja dokona zmianyw tablicy koszyka z zakupami, na przykład przez dodanie produktu,to inna funkcja używająca tej tablicy koszkacart będzie tą zmianą dotknięta.Może to być dobre, jednak może też być i złe. Wyobraź sobie złą sytuację:

Użytkownik klika przycisk "Kup" wywołujący funkcjępurchase, która tworzyżądanie i wysyła tablicęcart do serwera. Z powodusłabego połączenia sieciowego, funkcjapurchase musi powtarzać żądanie.Co jeśli w międzyczasie użytkownik przypadkowo kliknie przycisk "Dodaj do koszyka"na produkcie, który nie był dodany wcześniej?Jeśli tak się wydarzy i żądanie zostanie wysłane, wtedy funkcja kupującawyśle przypadkowo dodany produkt, gdyż posiada ona referencję do tablicy koszykazakupów zmodyfikowaną przez funkcjęaddItemToCart poprzez dodanie niechcianegoproduktu.

Świetnym rozwiązaniem byłoby, abyaddItemToCart zawsze klonowałacart,edytowała i zwracała sklonowaną tablicę. To zapewnia, że żadna inna funkcjaprzechowująca referencję do koszyka zakupów będzie dotknięta jakąkolwiek zmianą.

Dwa zastrzeżenia do tego podejścia:

  1. Mogą występować przypadki, w których rzeczywiście chcesz zmodyfikować wejściowy obiekt,ale gdy zaczniesz stosować tę praktykę w programowaniu, przekonasz się, że są onedość rzadkie. Większość może być zrefaktoryzowana bez skutków ubocznych!

  2. Klonowanie dużych obiektów może być kosztowne pod względem wydajności. Na szczęściew praktyce nie jest to duży problem, gdyż mamyświetne biblioteki pozwalającetakiemu podejściu do programowania być szybkim i nieobciążającym pamięci aż tak,jak by to było w przypadku ręcznego klonowania obiektów i tablic.

Źle:

constaddItemToCart=(cart,item)=>{cart.push({ item,date:Date.now()});};

Dobrze:

constaddItemToCart=(cart,item)=>{return[...cart,{ item,date:Date.now()}];};

⬆ powrót na początek

Nie pisz do funkcji globalnych

Zanieczyszczanie globalnej przestrzeni jest złą praktyką w języku JavaScript,gdyż możesz kolidować z inną biblioteką i użytkownik Twojego API może być niczego nieświadomym, dopóki wyjątek nie pojawi się na produkcji. Pomyślmy o takim przykładzie:co jeśli chciałbyś rozszerzyć natywny obiekt Array, aby miał metodędiff, któramoże pokazać różnicę między dwiema tablicami? Mógłbyś przypisać swoją nową funkcjędoArray.prototype, ale może to kolidować z inną biblioteką, która próbowałazrobić to samo. Co jeśli inna biblioteka używaładiff do znalezieniaróżnicy między pierwszym i ostatnim elementem tablicy? Właśnie dlategobyłoby dużo lepiej używać po prostu klas wprowadzonych w wersji ES2015/ES6i zwyczajnie rozszerzyć globalnąArray.

Źle:

Array.prototype.diff=functiondiff(comparisonArray){consthash=newSet(comparisonArray);returnthis.filter(elem=>!hash.has(elem));};

Dobrze:

classSuperArrayextendsArray{diff(comparisonArray){consthash=newSet(comparisonArray);returnthis.filter(elem=>!hash.has(elem));}}

⬆ powrót na początek

Przedkładaj programowanie funkcyjne nad programowanie imperatywne

JavaScript nie jest językiem funkcyjnym w takim stopniu, jak Haskell, ale zawiera trochęfunkcyjnego aromatu. Języki funkcyjne mogą być czystsze i prostsze w testowaniu.Preferuj ten styl programowania, kiedy tylko możesz.

Źle:

constprogrammerOutput=[{name:'Uncle Bobby',linesOfCode:500},{name:'Suzie Q',linesOfCode:1500},{name:'Jimmy Gosling',linesOfCode:150},{name:'Gracie Hopper',linesOfCode:1000}];lettotalOutput=0;for(leti=0;i<programmerOutput.length;i++){totalOutput+=programmerOutput[i].linesOfCode;}

Dobrze:

constprogrammerOutput=[{name:'Uncle Bobby',linesOfCode:500},{name:'Suzie Q',linesOfCode:1500},{name:'Jimmy Gosling',linesOfCode:150},{name:'Gracie Hopper',linesOfCode:1000}];consttotalOutput=programmerOutput.map(output=>output.linesOfCode).reduce((totalLines,lines)=>totalLines+lines);

⬆ powrót na początek

Stosuj hermetyzację warunków

Źle:

if(fsm.state==='fetching'&&isEmpty(listNode)){// ...}

Dobrze:

functionshouldShowSpinner(fsm,listNode){returnfsm.state==='fetching'&&isEmpty(listNode);}if(shouldShowSpinner(fsmInstance,listNodeInstance)){// ...}

⬆ powrót na początek

Unikaj negowania warunków

Źle:

functionisDOMNodeNotPresent(node){// ...}if(!isDOMNodeNotPresent(node)){// ...}

Dobrze:

functionisDOMNodePresent(node){// ...}if(isDOMNodePresent(node)){// ...}

⬆ powrót na początek

Unikaj warunków

Wydaje się to być zadaniem niewykonalnym. Większość ludzi słysząc to pierwszy raz, powie:"Jak mam zrobić cokolwiek bez instrukcjiif?" Odpowiedzią jest, żemożesz użyć polimorfizmu, aby osiągnąć to samo w wielu przypadkach. Drugimpytaniem jest zwykle: "Dobrze, to świetnie, ale dlaczego chciałbym to zrobić?"Odpowiedzią jest poprzednio poznana koncepcja czystego kodu: funkcja powinna robićtylko jedną rzecz. Gdy masz klasy i funkcje zawierające instrukcjeif,mówisz użytkownikowi, że Twoja funkcja robi więcej, niż jedną rzecz. Pamiętaj,rób po prostu jedną rzecz.

Źle:

classAirplane{// ...getCruisingAltitude(){switch(this.type){case'777':returnthis.getMaxAltitude()-this.getPassengerCount();case'Air Force One':returnthis.getMaxAltitude();case'Cessna':returnthis.getMaxAltitude()-this.getFuelExpenditure();}}}

Dobrze:

classAirplane{// ...}classBoeing777extendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude()-this.getPassengerCount();}}classAirForceOneextendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude();}}classCessnaextendsAirplane{// ...getCruisingAltitude(){returnthis.getMaxAltitude()-this.getFuelExpenditure();}}

⬆ powrót na początek

Unikaj sprawdzania typów (część 1)

JavaScript jest typowany dynamicznie, co oznacza, że Twoje funkcje mogą przyjmować argumenty dowolnego typu.Czasami ta wolność jest uciążliwa i sprawdzanie typów w Twoich funkcjachokazuje się kuszące. Jest wiele sposobów, aby tego uniknąć.Pierwszym jest rozważenie zwartych API.

Źle:

functiontravelToTexas(vehicle){if(vehicleinstanceofBicycle){vehicle.pedal(this.currentLocation,newLocation('texas'));}elseif(vehicleinstanceofCar){vehicle.drive(this.currentLocation,newLocation('texas'));}}

Dobrze:

functiontravelToTexas(vehicle){vehicle.move(this.currentLocation,newLocation('texas'));}

⬆ powrót na początek

Unikaj sprawdzania typów (część 2)

Jeśli pracujesz z podstawowymi wartościami jak ciągi znaków i tablicei nie możesz użyć polimorfizmu, a nadal czujesz potrzebę sprawdzania typów,powinieneś rozważyć użycie języka TypeScript. Jest on świetną alternatywą dla normalnegojęzyka JavaScript, gdyż dostarcza statycznego typowania do jego standardowej składni.Problemem z ręcznym sprawdzaniem typów w normalnym JavaScript jest to, że używanie gopoprawnie wymaga na tyle dużo dodatkowej rozwlekłości, iż otrzymane sztuczne bezpieczeństwo typologicznenie wynagradza utraty czytelności. Utrzymuj Twój JavaScript czystym, piszdobre testy i miej dobre przeglądy kodu. W przeciwnym razie rób wszystko to samo,ale używając TypeScript (który, jak powiedziałem, jest świetną alternatywą!).

Źle:

functioncombine(val1,val2){if(typeofval1==='number'&&typeofval2==='number'||typeofval1==='string'&&typeofval2==='string'){returnval1+val2;}thrownewError('Must be of type String or Number');}

Dobrze:

functioncombine(val1,val2){returnval1+val2;}

⬆ powrót na początek

Nie optymalizuj nadmiernie

Nowoczesne przeglądarki w czasie wykonania dokonują wielu optymalizacji "pod maską".Często gdy optymalizujesz, po prostu tracisz swój czas.Tu są dobre źródłapokazująca niedostatki optymalizacji. Postaw je sobie za cel w międzyczasie, dopóki nie będąpoprawione, jeśli mogą być.

Źle:

// W starych przeglądarkach każda iteracja z niebuforowanym `list.length` byłaby kosztowna// w związku z ponownym obliczeniem `list.length`. W nowoczesnych przeglądarkach jest to zoptymalizowane.for(leti=0,len=list.length;i<len;i++){// ...}

Dobrze:

for(leti=0;i<list.length;i++){// ...}

⬆ powrót na początek

Usuwaj martwy kod

Martwy kod jest po prostu tak samo zły, jak powielony kod. Nie ma powodu, aby go trzymać.Jeśli nie będzie wywoływany, pozbądź się go! Będzie nadal bezpiecznyw historii Twojego systemu wersjonowania, jeśli ciągle go potrzebujesz.

Źle:

functionoldRequestModule(url){// ...}functionnewRequestModule(url){// ...}constreq=newRequestModule;inventoryTracker('apples',req,'www.inventory-awesome.io');

Dobrze:

functionnewRequestModule(url){// ...}constreq=newRequestModule;inventoryTracker('apples',req,'www.inventory-awesome.io');

⬆ powrót na początek

Obiekty i struktury danych

Używaj getterów i setterów

Używanie getterów i setterów, aby uzyskać dostęp do danych obiektów, może być lepsze, niż zwykłesprawdzanie właściwości obiektu. Możesz spytać: "Dlaczego?". Hmmm... oto niektórez powodów:

  • Jeśli chcesz robić coś ponad pobieranie właściwości obiektu, nie musiszsprawdzać i zmieniać każdego akcesora w swoim kodzie.
  • Dodanie walidacji jest proste podczas wykonywaniaset.
  • Hermetyzuje wewnętrzną reprezentację.
  • Łatwo dodać logowanie i obsługę błędów podczas pobierania i ustawiania.
  • Możesz zastosować leniwe ładowanie właściwości Twojego obiektu, przykładowo, pobierając jez serwera.

Źle:

functionmakeBankAccount(){// ...return{balance:0,// ...};}constaccount=makeBankAccount();account.balance=100;

Dobrze:

functionmakeBankAccount(){// ta jest prywatnaletbalance=0;// "getter" udostępniony publicznie przez zwrócenie obiektu poniżejfunctiongetBalance(){returnbalance;}// "setter" udostępniony publicznie przez zwrócenie obiektu poniżejfunctionsetBalance(amount){// ... walidacja przed uaktualnieniem zmiennej "balance"balance=amount;}return{// ...    getBalance,    setBalance,};}constaccount=makeBankAccount();account.setBalance(100);

⬆ powrót na początek

Używaj prywatnych właściwości i metod obiektów

Można to osiągnąć dzięki domknięciom (dla wersji ES5 i niższych).

Źle:

constEmployee=function(name){this.name=name;};Employee.prototype.getName=functiongetName(){returnthis.name;};constemployee=newEmployee('John Doe');console.log(`Employee name:${employee.getName()}`);// Employee name: John Doedeleteemployee.name;console.log(`Employee name:${employee.getName()}`);// Employee name: undefined

Dobrze:

functionmakeEmployee(name){return{getName(){returnname;},};}constemployee=makeEmployee('John Doe');console.log(`Employee name:${employee.getName()}`);// Employee name: John Doedeleteemployee.name;console.log(`Employee name:${employee.getName()}`);// Employee name: John Doe

⬆ powrót na początek

Klasy

Przedkładaj klasy wprowadzone w ES2015/ES6 ponad proste funkcje jak w ES5

Trudno uzyskać czytelne dziedziczenie, konstrukcję i definicje metodklasycznymi technikami dostępnymi w ES5. Gdy potrzebujesz dziedziczenia (a bądź świadom,że może nie musisz), wykorzystuj klasy wprowadzone w ES2015/ES6. Niemniej jednak przedkładaj małe funkcje ponadklasy, dopóki nie będziesz potrzebował większych i bardziej złożonych obiektów.

Źle:

constAnimal=function(age){if(!(thisinstanceofAnimal)){thrownewError('Instantiate Animal with `new`');}this.age=age;};Animal.prototype.move=functionmove(){};constMammal=function(age,furColor){if(!(thisinstanceofMammal)){thrownewError('Instantiate Mammal with `new`');}Animal.call(this,age);this.furColor=furColor;};Mammal.prototype=Object.create(Animal.prototype);Mammal.prototype.constructor=Mammal;Mammal.prototype.liveBirth=functionliveBirth(){};constHuman=function(age,furColor,languageSpoken){if(!(thisinstanceofHuman)){thrownewError('Instantiate Human with `new`');}Mammal.call(this,age,furColor);this.languageSpoken=languageSpoken;};Human.prototype=Object.create(Mammal.prototype);Human.prototype.constructor=Human;Human.prototype.speak=functionspeak(){};

Dobrze:

classAnimal{constructor(age){this.age=age;}move(){/* ... */}}classMammalextendsAnimal{constructor(age,furColor){super(age);this.furColor=furColor;}liveBirth(){/* ... */}}classHumanextendsMammal{constructor(age,furColor,languageSpoken){super(age,furColor);this.languageSpoken=languageSpoken;}speak(){/* ... */}}

⬆ powrót na początek

Wykorzystuj łańcuchowanie metod

Wzorzec ten jest bardzo przydatny w języku JavaScript i widać to w wielu bibliotekach takich jakjQuery i Lodash. Pozwala on uzyskać kod ekspresywny i mniej rozwlekły.W związku z tym mówię - użyj łańcuchowania metod i popatrz, jak czysty będzie Twój kod.W funkcjach Twoich klas zwyczajnie zwracajthis na końcu każdej funkcjii będziesz mógł doczepić do nich (jak ogniwa łańcucha) inne metody klasy.

Źle:

classCar{constructor(make,model,color){this.make=make;this.model=model;this.color=color;}setMake(make){this.make=make;}setModel(model){this.model=model;}setColor(color){this.color=color;}save(){console.log(this.make,this.model,this.color);}}constcar=newCar('Ford','F-150','red');car.setColor('pink');car.save();

Dobrze:

classCar{constructor(make,model,color){this.make=make;this.model=model;this.color=color;}setMake(make){this.make=make;// ZAUWAŻ: Zwracamy this dla łańcuchowaniareturnthis;}setModel(model){this.model=model;// ZAUWAŻ: Zwracamy this dla łańcuchowaniareturnthis;}setColor(color){this.color=color;// ZAUWAŻ: Zwracamy this dla łańcuchowaniareturnthis;}save(){console.log(this.make,this.model,this.color);// ZAUWAŻ: Zwracamy this dla łańcuchowaniareturnthis;}}constcar=newCar('Ford','F-150','red').setColor('pink').save();

⬆ powrót na początek

Przedkładaj kompozycję ponad dziedziczenie

Jak stwierdzono głośno weWzorcach projektowych Bandy Czterech,powinieneś przedkładać kompozycję ponad dziedziczenie tam, gdzie tylko możesz. Jest wieledobrych powodów, aby używać dziedziczenia i wiele dobrych powodów, aby używać kompozycji.Głównym punktem tej maksymy jest, aby gdy w myślach instynktownie skłaniasz sięku dziedziczeniu, próbować zastanowić się, czy kompozycja może odwzorować problem lepiej.W niektórych przypadkach może.

Możesz się zastanawiać: "kiedy powinienem użyć dziedziczenia?". To zależyod Twojego problemu, ale tu jest skromna lista przypadków, gdy dziedziczeniema więcej sensu niż kompozycja:

  1. Twoje dziedziczenie reprezentuje relację "x-jest-y" a nie "x-posiada-y"(Człowiek->Zwierzę kontra Użytkownik->SzczegółyUżytkownika).
  2. Możesz wykorzystać ponownie kod ze zbioru swoich klas (ludzie mogą poruszać się jak wszystkie zwierzęta).
  3. Chcesz dokonać globalnych zmian w klasach pochodnych zmieniając klasę podstawową(zmienić zużycie kalorii podczas poruszania dla wszystkich zwierząt).

Źle:

classEmployee{constructor(name,email){this.name=name;this.email=email;}// ...}// Źle, gdyż Pracownicy "posiadają" dane podatkowe. DanePodatkowePracownika nie są typem PracownikaclassEmployeeTaxDataextendsEmployee{constructor(ssn,salary){super();this.ssn=ssn;this.salary=salary;}// ...}

Dobrze:

classEmployeeTaxData{constructor(ssn,salary){this.ssn=ssn;this.salary=salary;}// ...}classEmployee{constructor(name,email){this.name=name;this.email=email;}setTaxData(ssn,salary){this.taxData=newEmployeeTaxData(ssn,salary);}// ...}

⬆ powrót na początek

SOLID

Zasada jednej odpowiedzialności (SRP)

Jak stwierdzono w Czystym Kodzie, "Nigdy nie powinno być więcej niż jednego powodu do modyfikacji klasy". Kuszącym jest zapakowanie w klasę wielu funkcjonalności, tak jak wtedy,gdy możesz zabrać tylko jedną walizkę podczas lotu. Problemem jest tutaj to,że Twoja klasa nie będzie koncepcyjnie spójna i da jej to wiele powodówdo zmian. Minimalizacja ilości sytuacji, w których musisz zmienić klasę, jest istotna.Jest istotna, gdyż jeśli jedna klasa zawiera zbyt dużo funkcjonalności i modyfikujeszczęść z nich, może stać się trudnym do zrozumienia, jak wpłynie to na innezależne moduły w Twoim kodzie.

Źle:

classUserSettings{constructor(user){this.user=user;}changeSettings(settings){if(this.verifyCredentials()){// ...}}verifyCredentials(){// ...}}

Dobrze:

classUserAuth{constructor(user){this.user=user;}verifyCredentials(){// ...}}classUserSettings{constructor(user){this.user=user;this.auth=newUserAuth(user);}changeSettings(settings){if(this.auth.verifyCredentials()){// ...}}}

⬆ powrót na początek

Zasada otwarte-zamknięte (OCP)

Jak stwierdził Bertrand Meyer, "Encje (klasy, moduły, funkcje itd.)powinny być otwarte na rozszerzenie, ale zamknięte na modyfikacje". Co więcto oznacza? Ta zasada po prostu stwierdza, że powinieneś umożliwić użytkownikomdodanie nowych funkcjonalności bez zmiany istniejącego kodu.

Źle:

classAjaxAdapterextendsAdapter{constructor(){super();this.name='ajaxAdapter';}}classNodeAdapterextendsAdapter{constructor(){super();this.name='nodeAdapter';}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){if(this.adapter.name==='ajaxAdapter'){returnmakeAjaxCall(url).then((response)=>{// przekształć odpowiedź i zwróć});}elseif(this.adapter.name==='httpNodeAdapter'){returnmakeHttpCall(url).then((response)=>{// przekształć odpowiedź i zwróć});}}}functionmakeAjaxCall(url){// żądanie i zwrócenie obietnicy}functionmakeHttpCall(url){// żądanie i zwrócenie obietnicy}

Dobrze:

classAjaxAdapterextendsAdapter{constructor(){super();this.name='ajaxAdapter';}request(url){// żądanie i zwrócenie obietnicy}}classNodeAdapterextendsAdapter{constructor(){super();this.name='nodeAdapter';}request(url){// żądanie i zwrócenie obietnicy}}classHttpRequester{constructor(adapter){this.adapter=adapter;}fetch(url){returnthis.adapter.request(url).then((response)=>{// przekształć odpowiedź i zwróć});}}

⬆ powrót na początek

Zasada podstawienia Liskov (LSP)

Jest to przerażająca nazwa dla bardzo prostego pojęcia. Jest formalnie zdefiniowana jako "Jeśli Sjest podtypem T, wtedy obiekty typu T mogą być wymienione z obiektami typu S(np. obiekty typu S mogą zastąpić obiekty typu T) bez zmieniania żadnychpożądanych właściwości tego programu (poprawność, zadanie wykonaneitd.)". To jeszcze bardziej przerażająca definicja.

Najlepszym wyjaśnieniem tego będzie, jeśli masz klasę bazową i klasę potomną,wtedy klasy bazowa i potomna mogą zostać użyte wymiennie bez otrzymanianiepoprawnych wyników. Może to być nadal pogmatwane, spójrzmy więc naklasyczny przykład: Kwadrat-Prostokąt. Matematycznie kwadrat jest prostokątem, alejeśli modelujesz to używając relacji "x-jest-y" przez dziedziczenie, szybkowpadniesz w kłopoty.

Źle:

classRectangle{constructor(){this.width=0;this.height=0;}setColor(color){// ...}render(area){// ...}setWidth(width){this.width=width;}setHeight(height){this.height=height;}getArea(){returnthis.width*this.height;}}classSquareextendsRectangle{setWidth(width){this.width=width;this.height=width;}setHeight(height){this.width=height;this.height=height;}}functionrenderLargeRectangles(rectangles){rectangles.forEach((rectangle)=>{rectangle.setWidth(4);rectangle.setHeight(5);constarea=rectangle.getArea();// ŹLE: Zwraca 25 dla Kwadratu. Powinno być 20rectangle.render(area);});}constrectangles=[newRectangle(),newRectangle(),newSquare()];renderLargeRectangles(rectangles);

Dobrze:

classShape{setColor(color){// ...}render(area){// ...}}classRectangleextendsShape{constructor(width,height){super();this.width=width;this.height=height;}getArea(){returnthis.width*this.height;}}classSquareextendsShape{constructor(length){super();this.length=length;}getArea(){returnthis.length*this.length;}}functionrenderLargeShapes(shapes){shapes.forEach((shape)=>{constarea=shape.getArea();shape.render(area);});}constshapes=[newRectangle(4,5),newRectangle(4,5),newSquare(5)];renderLargeShapes(shapes);

⬆ powrót na początek

Zasada segregacji interfejsów (ISP)

JavaScript nie posiada interfejsów, więc ta zasada nie ma tu tak restrykcyjnego zastosowania,jak pozostałe. Mimo tego jest ważna i istotna nawet przy braku typowania w JavaScript.

ISP stwierdza, że "Na klientach nie powinna być wymuszana zależność od interfejsów,których oni nie używają". Interfejsy w JavaScript są niejawnymi kontraktami przezkacze typowanie.

Dobrym przykładem demonstrującym tę zasadę w JavaScript są klasy,które wymagają dużych obiektów z opcjami. Nie wymaganie od klientów instalowaniaogromnych ilości ustawień jest korzystne, gdyż przez większość czasu nie będą oni potrzebowaćwszystkich tych ustawień. Uczynienie ich opcjonalnymi pomoże zapobiec otrzymaniu"grubego interfejsu".

Źle:

classDOMTraverser{constructor(settings){this.settings=settings;this.setup();}setup(){this.rootNode=this.settings.rootNode;this.animationModule.setup();}traverse(){// ...}}const$=newDOMTraverser({rootNode:document.getElementsByTagName('body'),animationModule(){}// W większości przypadków nie będziemy musieli animować podczas trawersowania.// ...});

Dobrze:

classDOMTraverser{constructor(settings){this.settings=settings;this.options=settings.options;this.setup();}setup(){this.rootNode=this.settings.rootNode;this.setupOptions();}setupOptions(){if(this.options.animationModule){// ...}}traverse(){// ...}}const$=newDOMTraverser({rootNode:document.getElementsByTagName('body'),options:{animationModule(){}}});

⬆ powrót na początek

Zasada odwrócenia zależności (DIP)

Ta zasada określa dwie istotne rzeczy:

  1. Wysokopoziomowe moduły nie powinny zależeć od modułów niskopoziomowych. Jedne i drugiepowinny zależeć od abstrakcji.
  2. Abstrakcje nie powinny zależeć od szczegółów. Szczegóły powinny zależećod abstrakcji.

Z początku może to być trudne do zrozumienia, ale jeśli pracowałeś z AngularJS,widziałeś implementację tej zasady w formie Wstrzykiwania zależności (DI).Podczas gdy nie są one identycznymi pojęciami, Zasada odwrócenia zależności trzyma wysokopoziomowemoduły z dala od wiedzy na temat szczegółów ich niskopoziomowych modułów i ich ustawiania.Może to być osiągnięte dzięki Wstrzykiwaniu zależności. Ogromną korzyścią jest, iż redukuje tozależności między modułami. Zależność jest bardzo złym wzorcem, gdyżczyni Twój kod trudniejszym do zrefaktoryzowania.

Jak wcześniej zaznaczono, JavaScript nie posiada interfejsów, więc abstrakcjebędące zależnymi, są niejawnymi kontraktami. Oznacza to metodyi właściwości, które obiekt/klasa wystawia dla innego obiektu/klasy.W przykładzie poniżej niejawnym kontraktem jest to, że dowolny moduł Request dlaInventoryTracker będzie posiadał metodęrequestItems.

Źle:

classInventoryRequester{constructor(){this.REQ_METHODS=['HTTP'];}requestItem(item){// ...}}classInventoryTracker{constructor(items){this.items=items;// ŹLE: Utworzyliśmy zależność od specyficznej implementacji żądania.// Powinniśmy po prostu uczynić requestItems zależnym od metody: `request`this.requester=newInventoryRequester();}requestItems(){this.items.forEach((item)=>{this.requester.requestItem(item);});}}constinventoryTracker=newInventoryTracker(['apples','bananas']);inventoryTracker.requestItems();

Dobrze:

classInventoryTracker{constructor(items,requester){this.items=items;this.requester=requester;}requestItems(){this.items.forEach((item)=>{this.requester.requestItem(item);});}}classInventoryRequesterV1{constructor(){this.REQ_METHODS=['HTTP'];}requestItem(item){// ...}}classInventoryRequesterV2{constructor(){this.REQ_METHODS=['WS'];}requestItem(item){// ...}}// Przez skonstruowanie naszych zależności na zewnątrz i wstrzyknięcie ich, możemy łatwo// zastąpić nasz moduł żądania nowym, fantazyjnym, używającym WebSockets.constinventoryTracker=newInventoryTracker(['apples','bananas'],newInventoryRequesterV2());inventoryTracker.requestItems();

⬆ powrót na początek

Testowanie

Testowanie jest ważniejsze niż dostarczanie. Jeśli nie masz testów albo jest ichnieodpowiednia ilość, to za każdym razem dostarczając swój kod, nie będziesz pewnym,że czegoś nie popsułeś. Decyzja o tym, jaka ilość testów jest odpowiednia, należydo Twojego zespołu, ale pokrycie w 100% (wszystkie instrukcje i gałęzie) jest tym,co pozwoli osiągnąć wysoką pewność i święty spokój dewelopera. Oznacza to, żejako dodatek do posiadanego świetnego frameworka do testowania, musisz jeszcze użyćdobrego narzędzia pokrycia.

Nie ma usprawiedliwienia dla niepisania testów. Jest [mnóstwo dobrych frameworków testowych] (http://jstherightway.org/#testing-tools), znajdź więc ten, który Twój zespół preferuje.Kiedy go znajdziesz, wtedy postaw sobie za cel, aby zawsze pisać testydla każdej nowej funkcjonalności/modułu, który wprowadzasz. Jeśli preferowaną przez Ciebie metodą jestTest Driven Development (TDD), to świetnie, ale istotą jest po prostuupewnienie się, że osiągasz swoje cele dotyczące pokrycia przed wypuszczeniem jakiejkolwiek funkcjonalnościalbo refaktoryzacji już istniejącej.

Pojedynczy pomysł na test

Źle:

importassertfrom'assert';describe('MakeMomentJSGreatAgain',()=>{it('handles date boundaries',()=>{letdate;date=newMakeMomentJSGreatAgain('1/1/2015');date.addDays(30);assert.equal('1/31/2015',date);date=newMakeMomentJSGreatAgain('2/1/2016');date.addDays(28);assert.equal('02/29/2016',date);date=newMakeMomentJSGreatAgain('2/1/2015');date.addDays(28);assert.equal('03/01/2015',date);});});

Dobrze:

importassertfrom'assert';describe('MakeMomentJSGreatAgain',()=>{it('handles 30-day months',()=>{constdate=newMakeMomentJSGreatAgain('1/1/2015');date.addDays(30);assert.equal('1/31/2015',date);});it('handles leap year',()=>{constdate=newMakeMomentJSGreatAgain('2/1/2016');date.addDays(28);assert.equal('02/29/2016',date);});it('handles non-leap year',()=>{constdate=newMakeMomentJSGreatAgain('2/1/2015');date.addDays(28);assert.equal('03/01/2015',date);});});

⬆ powrót na początek

Współbieżność

Używaj Obietnic, a nie wywołań zwrotnych

Wywołania zwrotne nie są czyste i powodują nadmierne ilości zagnieżdżeń.W ES2015/ES6 Obietnice są wbudowanym, globalnym typem. Używaj ich!

Źle:

import{get}from'request';import{writeFile}from'fs';get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin',(requestErr,response)=>{if(requestErr){console.error(requestErr);}else{writeFile('article.html',response.body,(writeErr)=>{if(writeErr){console.error(writeErr);}else{console.log('File written');}});}});

Dobrze:

import{get}from'request';import{writeFile}from'fs';get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin').then((response)=>{returnwriteFile('article.html',response);}).then(()=>{console.log('File written');}).catch((err)=>{console.error(err);});

⬆ powrót na początek

Async/Await są jeszcze bardziej czyste niż Obietnice

Obietnice są bardzo czystą alternatywą dla wywołań zwrotnych, ale ES2017/ES8wprowadza async i await, które oferują jeszcze czystsze rozwiązanie.Wszystkim, czego potrzebujesz, jest funkcja poprzedzona słowem kluczowymasync i wtedy możesz pisać swoją logikę imperatywnie bez łańcucha funkcjizthen. Używaj tego, jeśli możesz skorzystać z funkcjonalności ES2017/ES8 już dziś!

Źle:

import{get}from'request-promise';import{writeFile}from'fs-promise';get('https://en.wikipedia.org/wiki/Robert_Cecil_Martin').then((response)=>{returnwriteFile('article.html',response);}).then(()=>{console.log('File written');}).catch((err)=>{console.error(err);});

Dobrze:

import{get}from'request-promise';import{writeFile}from'fs-promise';asyncfunctiongetCleanCodeArticle(){try{constresponse=awaitget('https://en.wikipedia.org/wiki/Robert_Cecil_Martin');awaitwriteFile('article.html',response);console.log('File written');}catch(err){console.error(err);}}

⬆ powrót na początek

Obsługa błędów

Wyrzucane błędy są czymś dobrym! Oznaczają, że w czasie wykonania zostało poprawniezidentyfikowane coś, co poszło źle w Twoim programie i daje Ci znać, abyzatrzymać wykonywanie funkcji na obecnym stosie, zamknąć proces (w Node)i poinformować Cię w konsoli ze śladem stosu.

Nie ignoruj przechwyconych błędów

Nie zrobienie niczego z przechwyconym błędem nie daje Ci możliwości naprawieniaalbo zareagowania na ten błąd. Logowanie błędu do konsoli (console.log)nie jest dużo lepsze, gdyż może on zaginąć w morzu rzeczy informacji do konsoli.Jeśli zawrzesz jakikolwiek kawałek kodu w blokutry/catch, oznacza to, żemyślisz o błędzie mogącym tam wystąpić i w związku z tym powinieneś mieć planalbo utworzyć ścieżkę kodu do miejsca, w którym wystąpi.

Źle:

try{functionThatMightThrow();}catch(error){console.log(error);}

Dobrze:

try{functionThatMightThrow();}catch(error){// Jedna z opcji (głośniejsza niż console.log):console.error(error);// Inna opcja:notifyUserOfError(error);// Inna opcja:reportErrorToService(error);// ALBO zastosuj wszystkie trzy!}

Nie ignoruj odrzuconych obietnic

Z tych samych powodów, dla których nie powinieneś ignorować przechwyconych błędówwtry/catch.

Źle:

getdata().then((data)=>{functionThatMightThrow(data);}).catch((error)=>{console.log(error);});

Dobrze:

getdata().then((data)=>{functionThatMightThrow(data);}).catch((error)=>{// Jedna z opcji (głośniejsza niż console.log):console.error(error);// Inna opcja:notifyUserOfError(error);// Inna opcja:reportErrorToService(error);// ALBO zastosuj wszystkie trzy!});

⬆ powrót na początek

Formatowanie

Formatowanie jest subiektywne. Jak w wielu innych tu przypadkach, nie ma sztywnej i szybkiejzasady, którą musisz przyjąć. Najważniejsze, abyś NIE SPIERAŁ SIĘ o formatowanie.Sątony narzędzi, by to zautomatyzować.Użyj jednego! Spieranie się o formatowanie jest stratą czasu i pieniędzydla inżynierów.

W sprawach, które nie są objęte automatycznym formatowaniem(wcięcia, tabulacje kontra spacje, podwójne kontra pojedyncze cudzysłowy itd.) zaglądnij tupo trochę wskazówek.

Używaj wielkich liter konsekwentnie

JavaScript jest typowany dynamicznie, więc wielkie litery powiedzą Ci dużo o Twoichzmiennych, funkcjach itd. Te zasady są subiektywne, więc Twój zespół może wybraćcokolwiek chce. Chodzi o to, żebyś bez względu na to, co wybierzecie, był konsekwentnym.

Źle:

constDAYS_IN_WEEK=7;constdaysInMonth=30;constsongs=['Back In Black','Stairway to Heaven','Hey Jude'];constArtists=['ACDC','Led Zeppelin','The Beatles'];functioneraseDatabase(){}functionrestore_database(){}classanimal{}classAlpaca{}

Dobrze:

constDAYS_IN_WEEK=7;constDAYS_IN_MONTH=30;constSONGS=['Back In Black','Stairway to Heaven','Hey Jude'];constARTISTS=['ACDC','Led Zeppelin','The Beatles'];functioneraseDatabase(){}functionrestoreDatabase(){}classAnimal{}classAlpaca{}

⬆ powrót na początek

Wywołanie funkcji i wywołana funkcja powinny być blisko siebie

Jeśli funkcja wywołuje inną, trzymaj te funkcje wertykalnie blisko w pliku źródłowym.Najlepiej umieść wywołanie zaraz powyżej wywołanej. Mamy tendencję do czytania koduz góry na dół jak gazetę. Dlatego też spraw, aby Twój kod był czytany w ten sposób.

Źle:

classPerformanceReview{constructor(employee){this.employee=employee;}lookupPeers(){returndb.lookup(this.employee,'peers');}lookupManager(){returndb.lookup(this.employee,'manager');}getPeerReviews(){constpeers=this.lookupPeers();// ...}perfReview(){this.getPeerReviews();this.getManagerReview();this.getSelfReview();}getManagerReview(){constmanager=this.lookupManager();}getSelfReview(){// ...}}constreview=newPerformanceReview(employee);review.perfReview();

Dobrze:

classPerformanceReview{constructor(employee){this.employee=employee;}perfReview(){this.getPeerReviews();this.getManagerReview();this.getSelfReview();}getPeerReviews(){constpeers=this.lookupPeers();// ...}lookupPeers(){returndb.lookup(this.employee,'peers');}getManagerReview(){constmanager=this.lookupManager();}lookupManager(){returndb.lookup(this.employee,'manager');}getSelfReview(){// ...}}constreview=newPerformanceReview(employee);review.perfReview();

⬆ powrót na początek

Komentarze

Komentuj tylko rzeczy mające złożoną logikę biznesową.

Komentarze są przeprosinami, nie wymogiem. Dobry kodprzeważnie dokumentuje się sam.

Źle:

functionhashIt(data){// Hashlethash=0;// Długość łańcucha znakówconstlength=data.length;// Pętla po literach w zmiennej datafor(leti=0;i<length;i++){// Pobierz kod litery.constchar=data.charCodeAt(i);// Utwórz hashhash=((hash<<5)-hash)+char;// Przekonwertuj na liczbę 32-bitowąhash&=hash;}}

Dobrze:

functionhashIt(data){lethash=0;constlength=data.length;for(leti=0;i<length;i++){constchar=data.charCodeAt(i);hash=((hash<<5)-hash)+char;// Przekonwertuj na liczbę 32-bitowąhash&=hash;}}

⬆ powrót na początek

Nie pozostawiaj zakomentowanego kodu

Kontrola wersji istnieje nie bez powodu. Pozostaw stary kod w Twojej historii.

Źle:

doStuff();// doOtherStuff();// doSomeMoreStuff();// doSoMuchStuff();

Dobrze:

doStuff();

⬆ powrót na początek

Nie twórz komentarzy-dziennika.

Pamiętaj, używaj kontroli wersji! Nie jest potrzebny martwy kod, zakomentowany kodi przede wszystkim komentarze będące dziennikiem. Używajgit log, by sprawdzić historię!

Źle:

/** * 2016-12-20: Usunąłem monady, nie rozumiałem ich (RM) * 2016-10-01: Ulepszyłem użycie specjalnych monad (JP) * 2016-02-03: Usunąłem sprawdzanie typów (LI) * 2015-03-14: Dodałem funkcję combine ze sprawdzaniem typów (JR) */functioncombine(a,b){returna+b;}

Dobrze:

functioncombine(a,b){returna+b;}

⬆ powrót na początek

Unikaj markerów pozycyjnych

Zwykle dodają one tylko szum. Pozwól funkcjom i nazwom zmiennych wraz z poprawnymiwcięciami i formatowaniem dać wizualną strukturę Twojemu kodowi.

Źle:

////////////////////////////////////////////////////////////////////////////////// Scope Model Instantiation////////////////////////////////////////////////////////////////////////////////$scope.model={menu:'foo',nav:'bar'};////////////////////////////////////////////////////////////////////////////////// Action setup////////////////////////////////////////////////////////////////////////////////constactions=function(){// ...};

Dobrze:

$scope.model={menu:'foo',nav:'bar'};constactions=function(){// ...};

⬆ powrót na początek

Tłumaczenie

Ten dokument dostępny jest również w innych językach:

⬆ powrót na początek

About

🛁 Clean Code concepts adapted for JavaScript (Polish) 🇵🇱 Czysty kod JavaScript, polskie tłumaczenie

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors82


[8]ページ先頭

©2009-2025 Movatter.jp