Roligt JavaScript: Inga lockiga hängslen. Argument: "koden fungerar bra"


JavaScript är vackert enkelt språk för dem som vill börja lära sig det, men det tar mycket ansträngning att behärska det.

Nybörjare gör ofta några vanliga misstag som kommer tillbaka för att hemsöka dem och dyker upp i det ögonblick när de minst anar det. För att förstå vad dessa fel är, läs den här artikeln.

1. Utelämnar lockiga hängslen

Ett av misstagen som JavaScript -nybörjare ofta gör är att utelämna lockiga hängslen efter uttalanden som om, annars, medan och för. Även om detta inte är förbjudet, måste du vara mycket försiktig eftersom det kan orsaka ett dolt problem och senare leda till ett fel.

Se exemplet nedan:

Js

// Denna kod gör inte vad den ska! if (namn === odefinierat) console.log ("Ange ett användarnamn!"); misslyckas (); // Exekvering når aldrig denna rad: framgång (namn); ) funktionsframgång (namn) (console.log ("Hej," + namn + "!");) funktion misslyckas () (kasta nytt fel ("Namn saknas. Kan inte säga hej!");)

Även om uppmaningen att misslyckas () är indragad och tycks tillhöra if -uttalandet, är det inte det. Det kallas alltid. Så det är en bra metod att omge alla kodblock med lockiga hängslen, även om det bara finns ett uttalande i dem.

2. Inga semikolon

Under JavaScript -analys, en process som kallas automatisk placering semikolon. Som namnet antyder placerar analysatorn de saknade tecknen åt dig.

Syftet med denna funktion är att göra JavaScript mer tillgängligt och lättare att skriva för nybörjare. Du måste dock alltid lägga till semikolon själv, eftersom det finns en risk att du missar det.

Här är ett exempel:

Js

// Resultatet av behandlingen av denna kod blir ett felmeddelande. Att lägga till ett semikolon skulle ha löst problemet. console.log ("Welcome the fellowship!") ["Frodo", "Gandalf", "Legolas", "Gimli"]. för Varje (funktion (namn) (hej (namn))) funktion hej (namn) (konsol. log ("Hej" + namn + "!"))

Eftersom rad 3 saknar ett semikolon antar parsern att den öppna parentesen på rad 5 är ett försök att komma åt en egenskap med hjälp av accessor -array -syntaxen (se fel # 8), inte en separat array, vilket inte är vad den skulle .

Detta resulterar i ett fel. Fixen är enkel - sätt alltid in ett semikolon.

Några erfarna JavaScript -utvecklare föredrar att inte använda semikolon, men de är väl medvetna om möjliga misstag och hur man förhindrar dem.

3. Missförstånd om typkonverteringar

JavaScript stöder dynamiska typer... Det betyder att du inte behöver ange typ när du deklarerar en ny variabel, du kan fritt tilldela eller konvertera dess värde.

Detta gör JavaScript mycket enklare än, säg, C # eller Java. Men detta är fullt av den potentiella risken för fel som upptäcks på andra språk vid sammanställningsstadiet.

Här är ett exempel:

Js

// Väntar på en inmatningshändelse från textrutan var textBox = document.querySelector ("input"); textBox.addEventListener ("input", function () (// textBox.value innehåller en sträng. Append 10 innehåller // string "10", men lägger inte till det .. console.log (textBox.value + " + 10 = " + (textBox.value + 10));));

Html

Problemet kan enkelt åtgärdas genom att använda parseInt (textBox.value, 10) för att konvertera strängen till ett tal innan du lägger till 10 till den.

Beroende på hur du använder variabeln kan körtiden bestämma att den måste konverteras till en viss typ. Detta kallas typecasting.

För att förhindra typkonvertering när du jämför variabler i en if -sats kan du använda strikt jämställdhetskontroll (=== ).

4. Glömt var

Ett annat misstag som nybörjare gör är att glömma att använda nyckelord var när variabler deklareras. JavaScript är en väldigt liberal motor.

Första gången den ser att du har använt en variabel utan var -sats kommer den automatiskt att deklarera den globalt. Detta kan leda till vissa fel.

Här är ett exempel som också illustrerar ett annat fel - ett saknat kommatecken när flera variabler deklareras samtidigt:

Js

var a = 1, b = 2, c = 3; funktionsalfabet (str) (var a = "A", b = "B" // Hoppsan, utelämnad här ","! c = "C", d = "D"; return str + "" + a + b + c + "...";) console.log (alfabetet ("Låt oss säga alfabetet!")); // Åh nej! Något är fel! c har ett nytt värde! console.log (a, b, c) ;

När parsern når rad 4 lägger den automatiskt till ett semikolon och tolkar sedan deklarationerna c och d på rad 5 som globala.

Detta kommer att ändra värdet på en annan variabel c. Mer om JavaScript fallgropar här.

5. Flytpunkts aritmetik

Detta fel är typiskt för nästan alla programmeringsspråk, inklusive JavaScript. På grund av sättet det flyttal visas i minnet, aritmetik görs inte exakt som du kanske tror.

Till exempel:

Js

var a = 0,1, b = 0,2; // Överraskning! Detta är fel: console.log (a + b == 0.3); // Eftersom 0.1 + 0.2 inte lägger till det tal du förväntade dig: console.log ("0.1 + 0.2 =", a + b);

För att undvika detta problem bör du inte använda decimaltal om du behöver absolut precision använder heltal, eller om du behöver arbeta med monetära enheter använder du ett bibliotek som bignumber.js.

6. Använda konstruktörer istället för originalbeteckningar

När Java- och C # -programmerare börjar skriva JavaScript föredrar de ofta att skapa objekt med konstruktörer: ny matris (), nytt objekt (), ny sträng ().

Js

var elem4 = new Array (1,2,3,4); console.log ("array med fyra element:" + elem4.length); // Skapa en matris från ett element. Det fungerar inte som du tror: var elem1 = new Array (23); console.log ("One element array?" + elem1.length); / * Strängobjekt har också sina egna egenskaper * / var str1 = new String ("JavaScript"), str2 = "JavaScript"; // Strikt jämlikhet tillämpas inte: console.log ("Är str1 samma som str2?", Str1 === str2);

Lösningen på detta problem är enkel: försök att alltid använda bokstavligt original
beteckningar. I JS är det inte heller nödvändigt att ange storleken på matriserna i förväg.

7. Missförstå hur områden delas

En av de svåra sakerna för nybörjare att förstå i JS är reglerna för avgränsning och stängning av intervall. Och det är verkligen inte lätt:

Js

för (var i = 0; i< 10; i++){ setTimeout(function(){ console.log(i+1); }, 100*i); } /* Чтобы исправить проблему, заключите код в выражение самовыполняющейся функции: for(var i = 0; i < 10; i++){ (function(i){ setTimeout(function(){ console.log(i+1); }, 100*i); })(i); } */

Funktioner upprätthåller deras koppling till variabler inom sina överordnade intervall. Men eftersom vi skjuter upp körningen genom setTimeout, när det är dags att starta funktionerna, kommer slingan faktiskt att slutföras och i -variabeln ökas till 11.

Den självkörande funktionen i kommentarerna fungerar eftersom den kopierar värdet på i och lagrar en kopia för varje fördröjning i funktionen. Du kan läsa mer om intervall här och här.

8. Använda Eval

Eval är ond. Detta anses vara dålig praxis, och i de flesta fall när du funderar på att tillämpa det, kan du hitta ett bättre och snabbare sätt.

Js

// Detta är dålig praxis. Gör inte detta: console.log (eval ("obj.name +" är en " + obj." + Access)); // Använd istället annotationsmatrisen för att dynamiskt komma åt egenskaper: console.log (obj.name + "är a" + obj); / * Använda eval i setTimout * / // Detta är också dålig praxis. Det är långsamt och svårt att testa och felsöka: setTimeout ("if (obj.age == 30) console.log (" This is eval-ed code, " + obj +"! ");", 100); // Bättre på detta sätt: setTimeout (function () (if (obj.age == 30) (console.log ("Denna kod är inte utvärderad," + obj + "!");)), 100);

Koden inuti eval är en sträng. Felsökningsmeddelanden relaterade till Eval -block är obegripliga, och du måste pussla dig själv för att korrekt placera enkla och dubbla citat.

För att inte tala om det kommer det att gå långsammare än vanligt JavaScript. Använd inte Eval om du inte vet exakt vad du gör.

9. Missförstånd av asynkron kod

Det som verkligen gör att JavaScript sticker ut och är unikt är att nästan alla element fungerar asynkront, och du måste skicka återuppringningar för att få händelsemeddelanden.

Det är fortfarande svårt för nybörjare att förstå detta intuitivt, och de blir snabbt stumpade när de ställs inför fel som är svåra att förstå.

Här är ett exempel där jag använder FreeGeoIP -tjänsten för att bestämma din plats med IP -adress:

Js

// Definiera platsdata nuvarande användaren... load (); // Visa användarens plats. Oj, det fungerar inte! Varför? console.log ("Hej! Din IP -adress är" + userData.ip + "och ditt land är" + userData.country_name); // Den laddade funktionen bestämmer ip för den aktuella användaren och deras plats // via ajax med freegeoip -tjänsten. När det är klart placeras den returnerade // -datan i variabeln userData. function load () ($ .getJSON ("http://freegeoip.net/json/?callback=?", function (response) (userData = response; // Mata ut följande rad från kommentarerna för att se den returnerade // resultat: // console.log (svar);));)

Även om console.log finns efter belastningsfunktionsanropet, körs det faktiskt innan data definieras.

10. Missbruk av händelsesspårning

Låt oss anta att du vill spåra ett knappklick, men bara om rutan är installerad.

Låt oss säga att jag skriver

If (someVal) alert ("True");

Sedan kommer nästa utvecklare in och säger, "Åh, jag måste göra något annat", så skriver de

If (someVal) alert ("True"); alert ("AlsoTrue");

Nu, som du kan se, kommer "AndTrue" alltid att vara sant eftersom den ursprungliga utvecklaren inte använde lockiga hängslen.

2018-12-04T00: 00Z

Absolut ja

Glöm "Detta är personliga preferenser", "koden kommer att fungera bra", "det fungerar bra för mig", "det är mer läsbart" yada yada BS.

Argument: "Detta är personlig preferens"

Nej. Om du inte är den enda personen som går ut till Mars, nej. För det mesta kommer det att finnas andra som läser / ändrar din kod. I alla seriösa kodningsteam är detta det rekommenderade sättet, så det är inte "personlig preferens".

Argument: "koden fungerar bra"

Så är spagettikoden! Betyder detta att det är ok att skapa det?

Argument: "Det fungerar bra för mig"

Under min karriär har jag sett så många buggar som skapats på grund av detta problem. Du kommer nog inte ihåg hur många gånger du kommenterade "DoSomething ()" och är förvirrad över varför "SomethingElse ()" kallas:

Om (skick) DoSomething (); Något annat ();

Om (skick) DoSomething (); Något mer ();

Här är ett exempel verkliga livet... Någon ville aktivera all loggning så att de kör hitta och ersätt "Console.println" => //"Console.println ":

If (villkor) Console.println ("något"); Något annat ();

Se problem?

Även om du tänker, "Det här är så trivialt att jag aldrig kommer att göra det"; kom ihåg att det alltid kommer att finnas en teammedlem med sämre programmeringskunskaper än du (förhoppningsvis är du inte den sämsta i laget!)

Argument: "det är mer läsbart"

Om jag har lärt mig något om programmering är det väldigt enkelt. Det är mycket vanligt att dessa är:

Om (skick) DoSomething ();

blir till följande efter att det har testats med olika webbläsare/ miljöer / användningsfall eller nya funktioner tillagda:

If (a! = Null) if (condition) DoSomething (); annars DoSomethingElse (); DoSomethingMore (); annars if (b == null) varning ("fel b"); annars varning ("fel a");

Och jämför det med detta:

If (a! = Null) (if (condition) (DoSomething ();) else (DoSomethingElse (); DoSomethingMore ();)) else if (b == null) (alert ("error b");) else ( varning ("fel a");)

PS: Bonuspoäng går till den som märkte felet i exemplet ovan

2018-12-11T00: 00Z

Förutom anledningen som nämns av @Josh K (som också gäller Java, C, etc.), En av speciella problem i JavaScript är automatisk semikoloninsättning. Från ett exempel på Wikipedia:

Returnera a + b; // Returnerar odefinierad. Behandlas som: // retur; // a + b;

Således kan det också ge oväntade resultat om det används så här:

Om (x) returnerar a + b;

Det är egentligen inte mycket bättre att skriva

Om (x) (return a + b;)

men kanske är felet här lite lättare att upptäcka (?)

2018-12-18T00: 00Z

Frågan ställer sig till enradiga uttalanden. Många exempel ger dock skäl att inte lämna lockiga hängslen på flera linjer. Det är helt säkert att inte använda parenteser på samma rad om det är den kodningsstil du föredrar.

Till exempel ställer frågan om detta är normalt:

Om (villkor) uttalande;

Han frågar inte om allt är okej:

Om (villkor) uttalande;

Jag tror att lämna parenteserna är att föredra eftersom det gör koden mer läsbar med mindre onödig syntax.

Min kodningsstil är att aldrig använda parenteser om inte koden är ett block. Och använd aldrig flera påståenden på samma rad (separerade med semikolon). Jag tycker att det är lätt att läsa och rengöra och har aldrig problem med "if" -uttalanden. Som ett resultat kan du använda parenteser i en om villkoret kräver 3 rader. Så här:

Om (villkor) (uttalande;)

Att använda en enda rad om operatören föredras eftersom den använder mindre vertikalt utrymme och koden är mer kompakt.

Jag skulle inte tvinga andra att använda den här metoden, men det fungerar för mig och jag kunde inte längre hålla med om exemplen som ges i hur man utesluter parenteser på grund av kodnings- / omfattningsfel.

2018-12-25T00: 00Z

Nej

Detta är helt sant

Om (kond) varning ("Villkor uppfyllt!") Annat varning ("Villkor inte uppfyllt!")

Samma praxis följs i alla Bindningsbara C -syntaxstilspråk. C, C ++, Java, till och med PHP stöder alla en enda radoperatör utan parenteser. Du måste förstå att du bara sparar två karaktärer, och med vissa stilar för vissa människor, du inte ens hålla linjen. Jag föredrar hela den lockiga stagstilen (som följande), så det brukar vara lite längre. Avvägningen kommer mycket bra med att ha mycket tydlig kodläsbarhet.

If (kond) (varning ("Villkor uppfyllt!")) Annat (varning ("Villkor inte uppfyllt!"))

2019-01-01T00: 00Z

Första nivån indragningen av ett uttalande måste vara lika med antalet öppna lockiga hängslen ovanför det. (exklusive citerade eller kommenterade lockiga hängslen eller symboler i förbehandlingsdirektiv)

Annars skulle K&R vara bra indrag. För att korrigera deras stil rekommenderar jag att du lägger ut kort enkla operatörer om på en rad.

Har alltid förvånat mig JavaScript först och främst det faktum att det förmodligen, som inget annat spritt språk, samtidigt stöder båda paradigmen: normal och onormal programmering. Och om nästan allt har lästs om adekvata bästa metoder och mallar, så är den fantastiska världen om hur du inte ska skriva kod, men du kan, bara lite öppen.


I denna artikel kommer vi att analysera ett annat långsökt problem som kräver oförlåtligt missbruk av en normal lösning.


Tidigare uppgift:

Ordvalet

Implementera en dekoratörsfunktion som räknar antalet samtal till den passerade funktionen och ger möjlighet att hämta det numret på begäran. I beslutet förbjuden använda lockiga hängslen och globala variabler.

Samtalsräknaren är bara en ursäkt, eftersom det finns console.count (). Slutsatsen är att vår funktion samlar in lite data när den inslagna funktionen anropas och ger någon form av gränssnitt för att komma åt den. Detta kan vara att spara alla resultat av ett samtal, samla loggar och någon form av memoization. Det är bara att disken är primitiv och begriplig för alla.


Hela svårigheten ligger i en onormal begränsning. Du kan inte använda lockiga hängslen, vilket innebär att du måste se över vardagliga rutiner och vanlig syntax.

En välbekant lösning

Först måste du välja en utgångspunkt. Vanligtvis, om språket eller dess tillägg inte ger nödvändig funktion dekoration, vi implementerar någon behållare själva: den inslagna funktionen, den ackumulerade data och gränssnittet för åtkomst till dem. Ofta är detta en klass:


klass CountFunction (konstruktör (f) (this.calls = 0; this.f = f;) anropa () (this.calls + = 1; returnera this.f (... argument);)) const csum = ny CountFunction ((x, y) => x + y); csum.invoke (3, 7); // 10 csum.invoke (9, 6); // 15 csum.calls; // 2

Detta kommer inte att fungera för oss direkt, eftersom:

  1. I JavaScript kan du inte implementera en privat egendom på detta sätt: vi kan båda läsa samtal instans (vilket är vad vi behöver) och skriver ett värde till det utifrån (som vi INTE behöver). Naturligtvis kan vi använda en stängning i konstruktören, men vad är då poängen med klassen? Och jag skulle fortfarande vara rädd för att använda färska privata fält utan babel 7.
  2. Språket stöder ett funktionellt paradigm och instantiering via ny det verkar inte här den bästa lösningen... Det är trevligare att skriva en funktion som returnerar en annan funktion. ja!
  3. Slutligen syntaxen Klassförklaring och Metod Definition kommer inte att tillåta oss att bli av med alla lockiga hängslen om vi vill.

Men vi har en bra som implementerar integritet med en stängning:


funktionsantal (f) (låt samtal = 0; return (anropa: function () (samtal + = 1; returnera f (... argument);), getCalls: function () (returnera samtal;));) const csum = count ((x, y) => x + y); csum.invoke (3, 7); // 10 csum.invoke (9, 6); // 15 csum.getCalls (); // 2

Du kan redan arbeta med detta.

En underhållande lösning

Vad används lockiga hängslen till här egentligen? Det här är fyra olika fall:

  1. Definitionen av funktionskroppens kropp ( Funktion Förklaring)
  2. Returnerad objektinitialisering
  3. Definitionen av funktionskroppen åberopar ( Funktionsuttryck) med två uttryck
  4. Kroppsdefinitionen för getCalls ( Funktionsuttryck) med ett uttryck

Låt oss börja med andra Artikel. Faktum är att vi inte behöver returnera ett nytt objekt, samtidigt som vi komplicerar uppropet till den slutliga funktionen via åberopa... Vi kan dra fördel av det faktum att en funktion i JavaScript är ett objekt, vilket innebär att den kan innehålla sin egen egna fält och metoder. Låt oss skapa vår returfunktion df och lägg till en metod getCalls, som genom stängningen kommer att ha tillgång till samtal som förut:


antal funktioner (f) (låt samtal = 0; funktion df () (samtal + = 1; returnera f (... argument);) df.getCalls = funktion () (återvända samtal;) returnera df;)

Det är trevligare att arbeta med detta:


const csum = count ((x, y) => x + y); csum (3, 7); // 10 csum (9, 6); // 15 csum.getCalls (); // 2

C fjärde punkt är allt klart: vi kommer helt enkelt att ersätta FunktionsuttryckArrowFunction... Frånvaron av lockiga hängslen ger oss en kort notering av en pilfunktion vid ett enda uttryck i kroppen:


funktionsantal (f) (låt samtal = 0; funktion df () (samtal + = 1; returnera f (... argument);) df.getCalls = () => samtal; returnera df;)

MED tredje- allt är mer komplicerat. Kom ihåg att det första vi gjorde var att byta ut Funktionsuttryck fungera åberopaFunktionDeklaration df... Att skriva om detta till ArrowFunction du måste lösa två problem: att inte förlora åtkomsten till argumenten (nu är det en pseudo-array argument) och fly funktionskroppen från två uttryck.


Parametern som uttryckligen anges för funktionen hjälper oss att hantera det första problemet. args med spridningsoperatör... Och för att kombinera två uttryck till ett kan du använda logiskt OCH... Till skillnad från den klassiska logiska konjunktionsoperatorn, som returnerar en boolean, utvärderar den operanderna från vänster till höger upp till den första "falska" och returnerar den, och om alla "sanna" - då det sista värdet. Den första ökningen av räknaren ger oss 1, vilket innebär att detta subuttryck alltid kommer att kastas till sanning. Vi är inte intresserade av reducerbarheten av resultatet av ett funktionsanrop i det andra subuttrycket: räknaren stannar ändå vid det. Vi kan nu använda ArrowFunction:


funktionsantal (f) (låt samtal = 0; låt df = (... args) => (samtal + = 1) && f (... args); df.getCalls = () => samtal; returnera df; )

Du kan dekorera inlägget lite med förhöjning av prefix:


funktionsantal (f) (låt samtal = 0; låt df = (... args) => ++ samtal && f (... args); df.getCalls = () => samtal; returnera df;)

Lösning den första och den svåraste punkten, låt oss börja med att byta ut Funktion FörklaringArrowFunction... Men för närvarande kommer vi att ha en kropp i lockiga hängslen:


const count = f => (låt samtal = 0; låt df = (... args) => ++ samtal && f (... args); df.getCalls = () => samtal; returnera df;);

Om vi ​​vill bli av med de lockiga hängslen som omger funktionskroppen måste vi undvika att deklarera och initiera variabler via låta... Och vi har två variabler: samtal och df.


Låt oss ta itu med disken först. Vi kan skapa en lokal variabel genom att definiera den i funktionsparameterlistan och skicka det initiala värdet genom att anropa med IIFE (Omedelbart åberopad funktionsuttryck):


const count = f => (samtal => (låt df = (... args) => ++ samtal && f (... args); df.getCalls = () => samtal; returnera df;)) ( 0);

Det återstår att sammanfoga tre uttryck till ett. Eftersom vi har alla tre uttrycken är funktioner som alltid kastas till sanning, kan vi också använda logiskt OCH:


const count = f => (samtal => (df = (... args) => ++ samtal && f (... args)) && (df.getCalls = () => samtal) && df) (0 );

Men det finns ett annat alternativ för att sammanfoga uttryck: att använda kommaoperatör... Det är att föredra eftersom det inte hanterar onödiga logiska konverteringar och kräver färre parenteser. Operander utvärderas från vänster till höger, och resultatet är värdet av den senare:


const count = f => (samtal => (df = (... args) => ++ samtal && f (... args), df.getCalls = () => samtal, df)) (0);

Kanske lyckades jag lura dig? Vi blev frimodigt av med variabeldeklarationen df och lämnade bara tilldelningen av vår pilfunktion. I detta fall kommer denna variabel att deklareras globalt, vilket är oacceptabelt! Låt oss upprepa för df initialisering av en lokal variabel i parametrarna för vår IIFE -funktion, men vi kommer inte att passera något initialvärde:


const count = f => ((samtal, df) => (df = (... args) => ++ samtal && f (... args), df.getCalls = () => samtal, df)) (0);

Således uppnås målet.

Varianter på ett tema

Intressant nog kunde vi undvika att skapa och initiera lokala variabler, flera uttryck i funktionsblock och skapa ett objekt bokstavligt. Samtidigt bevarades renheten i den ursprungliga lösningen: frånvaron av globala variabler, räknarens integritet, tillgång till argumenten för den inslagna funktionen.


I allmänhet kan du ta vilken implementering som helst och försöka göra något liknande. Till exempel en polyfyllning för funktionen binda i detta avseende är ganska enkelt:


const bind = (f, ctx, ... a) => (... args) => f.apply (ctx, a.concat (args));

Men om argumentet fär inte en funktion, på ett vänligt sätt bör vi göra ett undantag. Och undantaget kasta kan inte kastas i ett uttrycks sammanhang. Du kan vänta på kastuttryck (steg 2) och försöka igen. Eller har någon redan tankar?


Eller överväga en klass som beskriver koordinaterna för en punkt:


klass Point (konstruktör (x, y) (this.x = x; this.y = y;) toString () (return `($ (this.x), $ (this.y))`;))

Som kan representeras av en funktion:


const point = (x, y) => (p => (px = x, py = y, p.toString = () => ["(", x, ",", y, ")"]. gå med (""), p)) (nytt objekt);

Bara vi har förlorat prototypiskt arv här: att strängaär en egenskap hos prototypobjektet Punkt snarare än ett separat skapat objekt. Kan du undvika detta om du försöker nog?


I resultaten av transformationerna får vi en ohälsosam blandning av funktionell programmering med nödvändiga hackar och några funktioner i själva språket. Om du tänker efter kan det här visa sig vara en intressant (men inte praktisk) obfuscator. källkod... Du kan komma med din egen version av "parentes obfuscator" -problemet och underhålla dina kollegor och vänner med JavaScript gratis från användbart arbete tid.

Slutsats

Frågan är, vem är det användbart och varför behövs det? Detta är helt skadligt för nybörjare, eftersom det bildar en falsk uppfattning om språkets alltför komplexa och avvikande. Men det kan vara användbart för utövare, eftersom det låter dig titta på språkets särdrag från andra sidan: uppmaningen är inte att undvika, utan uppmaningen att försöka för att undvika i framtiden.

Etiketter: Lägg till taggar

Fel i ett program är svåra att hitta. Och för att göra det lättare för dig själv måste du skriva koden korrekt och vackert (läsbar). God kod måste vara läsbar, konsekvent, förutsägbar, välformad och väldokumenterad.

Viktiga punkter om kodens utformning

Indrag

Om du inte indragit är koden omöjlig att läsa. Vanligtvis görs indragning med en tabbtecken, men det finns de som indrag med mellanslag, mestadels 3-4 mellanslag. Följande är ett exempel på korrekt och felaktig indragning:

/ * Korrekt inryckning * /

< max; i++) {
om (arr [i]% 2 === 0) (
svar = (
summa: summa + = arr [i]
};
}
}
/ * Och allt är dåligt här * /
var svar, arr =, summa = 0;
för (var i = 0, max = arr. längd; i< max; i++) {
om (arr [i]% 2 === 0) (
svar = (
summa: summa + = arr [i]
};
}
}

Tandställning

Använd alltid lockiga hängslen, även om du inte behöver dem. Ur teknisk synvinkel, om kroppen av en om, medan eller för struktur består av ett uttalande, kan de lockiga hängslen utelämnas. Alltid skrivna lockiga hängslen gör det lättare att göra ändringar senare. Här är ett exempel på hur du gör det rätt och hur inte:

/ * Rätt post * /
för (var i = 0; i< 5; i++) {
varning (i);
}
/ * Det finns inget sådant * /
för (var i = 0; i< 5; i++)
varning (i);

Placering av den öppna parentesen

Det finns två typer av programmerare:

Om (a> b) (
...
}
om (a> b)
{
...
}

Hur man skriver öppningens lockiga hängslen är upp till dig. Vi vet redan att JavaScript själv ersätter semikolon och det är här problemet uppstår i den andra metoden. Låt oss säga att du skriver en kod så här:

Lämna tillbaka
{
id: 1
};

Och vad ska tolken göra? Han ersätter ett semikolon efter återkomst och det visar sig att funktionen kommer att återvända odefinierad. Det vill säga, tolken ser denna kodbit så här:

Retur odefinierad;
{
id: 1
};

Utrymmen

Utrymmen måste användas korrekt. När du skriver på papper lämnar du efter komma tomt utrymme? Jag antar att det är ja. Och när du anger något i JavaScript måste du skriva ett mellanslag efter kommatecken. Var ska du placera ett mellanslag för att göra koden begriplig?

1. Efter semikolon i for -uttalandet

För (var i = 0; i< 100; i++) {...}

2. Vid deklaration av flera variabler i en for -sats

För (var i = 0, max = arr. Längd; i< max; i++) {...}

3. Efter kommatecken, mellan elementen i matrisen

Den tredje delen av JavaScript -kodhandledningen. Enkla riktlinjer för att skapa ett lätt underhållbart projekt.

Undvik implicit typkonvertering

JavaScript innebär att man konverterar typerna av variabler när man jämför dem. Därför returnerar jämförelser som falskt == 0 eller "" == 0 sant.

För att undvika problem som orsakas av underförstådda konverteringar, använd alltid === och! == operatorerna för att testa både värdena och typerna av de uttryck som jämförs:

Det finns en annan programmeringsskola där det är allmänt accepterat att det är överanvändning av === operatören när == operatören är tillräcklig. Till exempel, när typof används, som returnerar en sträng, finns det ingen anledning att kräva en hård matchning. Men JSLint kräver strikt efterlevnad. Och dessutom kommer koden att se mer sammanhängande ut och minska tankemängden vid läsning (" Denna operatör== används avsiktligt eller av misstag? ").

Undvik att använda eval ()

Funktionen eval () tar en godtycklig sträng och kör den som JavaScript -kod. Om koden är känd (och inte bestäms under genomförandet av processen), finns det ingen som helst anledning att använda eval (). Om koden genereras dynamiskt vid körning är det ofta möjligt att uppnå målet den bästa metoden, hur med hjälp av eval() Till exempel är det bättre och enklare att använda fyrkantiga parenteser för att komma åt egenskaper dynamiskt:

// dålig var egenskap = "namn"; alert (eval ("obj." + property)); // det är att föredra att göra denna var property = "name"; varning (obj);

Att använda eval () kan också ha säkerhetsimplikationer, eftersom du kan köra kod (till exempel hämtad från nätet) som är skadlig. Det är en ganska vanlig ond metod att arbeta med ett JSON -svar på en AJAX -begäran. V det här fallet det är bättre att använda inbyggda webbläsarmetoder för att analysera JSON-svaret för att lösa problemet säker metod och rätt. För webbläsare som inte stöder JSON.parse () kan biblioteket från JSON.org användas.

Det är också viktigt att komma ihåg att överföring av strängar till setInterval (), setTimeout () och Function () -konstruktorn liknar att använda eval () i de flesta fall. Därför bör sådana åtgärder undvikas. I bakgrunden utvärderar och kör JavaScript de rader du skickar som programkod:

// dålig setTimeout ("myFunc ()", 1000); setTimeout ("myFunc (1, 2, 3)", 1000); // helst setTimeout (myFunc, 1000); setTimeout (funktion () (myFunc (1, 2, 3);), 1000);

Att använda den nya konstruktören nya funktion () liknar eval () och bör hanteras med försiktighet. den kraftfullt verktyg men missbrukas ofta. Om du absolut måste använda eval (), överväg att använda den nya funktionen () istället. Det finns en liten potentiell fördel, eftersom koden som definieras i nya Function () kommer att köras i det lokala funktionsutrymmet, så variabler som definieras med var -direktivet i den definierade koden kommer inte automatiskt att bli globala. Ett annat sätt att undvika automatisk upptäckt globala variabler - linda uppropet till eval () i en funktion.

Tänk på följande exempel. Här är bara un en global variabel som förorenar namnutrymmet:

Console.log (typ av un); // "odefinierad" console.log (typ av deux); // "odefinierad" console.log (typ av trois); // "odefinierad" var jsstring = "var un = 1; console.log (un);"; eval (jsstring); // Loggar "1" jsstring = "var deux = 2; console.log (deux);"; ny funktion (jsstring) (); // Loggar "2" jsstring = "var trois = 3; console.log (trois);"; (function () (eval (jsstring);) ()); // Loggar "3" console.log (typ av un); // nummer console.log (typ av deux); // odefinierad console.log (typ av trois); // odefinierad

En annan skillnad mellan eval () och den nya Function () -konstruktorn är att eval () kan korsa namnområdeskedjan, medan funktionskörning sker i sandlådan. Det spelar ingen roll var du kör funktion, den använder bara det globala namnområdet. Därför förorenar det det lokala namnområdet mindre. V följande exempel eval () kan komma åt och ändra variabler i sitt externa namnområde, men funktionen kan inte (användningen av funktion och ny funktion är identisk):

(function () (var local = 1; eval ("local = 3; console.log (local)"); // Loggar "3" console.log (local); // Logs "3") ()); (function () (var local = 1; Function ("console.log (typeof local);") (); // Loggar "odefinierad") ());

Konvertera ett tal med parseInt ()

Genom att använda parseInt () kan du få ett tal från en sträng. Funktionen tar en andra parameter, radixen, som ofta utelämnas. Men förgäves. Problemet manifesterar sig när det är nödvändigt att analysera en sträng som börjar med 0: till exempel en del av datumet som anges i ett formulärfält. En rad som börjar med 0 behandlas som oktalt tal(radix 8), som definierades i ECMAScript 3 (men ändrades i ECMAScript 5). För att undvika inkompatibilitet och oväntade resultat bör du alltid använda radixparametern:

Var månad = "06", år = "09"; månad = parseInt (månad, 10); år = parseInt (år, 10);

V detta exempel om du utelämnar radixparametern (anropa funktionen som parseInt (år)) får du värdet 0, eftersom “09” antas vara ett oktalt tal (som om du ringde till parseInt (år, 8)) , och 09 är inte korrekt bas 8.

Alternativa metoder konvertera en sträng till ett tal:

+ "08" // resultatet är 8 nummer ("08") // 8

Dessa metoder är ofta snabbare än parseInt () eftersom parseInt () gör stränganalyser snarare än enkel konvertering. Men om du antar att ingången kan vara "08 hej", returnerar parseInt () ett tal och andra metoder misslyckas med NaN.

Kodkrav

Det är viktigt att skriva krav för koden och följa dem - ett sådant steg kommer att göra koden sammanhängande, förutsägbar och betydligt lättare och mer begriplig. Ditt teams nya utvecklare efter kodkrav är viktigt kommer in snabbare till en fungerande rytm, uppfattar koden skriven av andra projektdeltagare.

Allvarliga skärmdumpar och uppgörelser uppstår under diskussionen olika aspekter kodkrav (till exempel vad man ska göra med indragning - mellanslag eller flikar). Därför, när du inför krav för koden, måste du på allvar förbereda dig för diskussioner. Men kraven för koden och strikt efterlevnad av dem är mycket viktiga för projektets existens och framgångsrika utveckling.

Indrag

Vissa utvecklare föredrar att använda flikar för indragning, eftersom alla kan anpassa sin redigerare för att visa ett visst antal mellanslag istället för flikar efter deras preferenser. Andra föredrar att använda mellanslag. När det gäller frågan är detta inte viktigt, huvudsaken är att strecksatserna definieras i de nuvarande kraven för koden.

Var ska du dra in? Regeln är enkel - varhelst det finns lockiga hängslen. Det vill säga, i kroppen av funktioner, slingor (gör, medan, för, för-in), if och byta uttalanden och objekt. Följande kod visar exempel på användning av indrag:

Funktion yttre (a, b) (var c = 1, d = 2, inre; om (a> b) (inre = funktion () (retur (r: c - d););) else (inre = funktion ( ) (retur (r: c + d););) återvänd inre;)

Tandställning

Lockiga hängslen bör alltid användas, även om de är alternativ. Tekniskt sett, när du bara har ett uttryck i ett if eller för uttalande, krävs inte lockiga hängslen, men du bör använda dem ändå. De gör din kod mer konsekvent och lättare att förlänga.

Låt oss säga att du har en loop med ett uttryck. Du kan utelämna parentesen, vilket inte gör det syntaxfel:

// dåligt för (var i = 0; i< 10; i += 1) alert(i);

Men vad händer om du senare måste lägga till en annan rad i slingans kropp?

// dåligt för (var i = 0; i< 10; i += 1) alert(i); alert(i + " is " + (i % 2 ? "odd" : "even"));

Den andra varningsfunktionen är utanför slingan, och indrag kan spela ett dåligt trick på dig. Det är bäst att alltid använda parenteser för perspektiv, även för ett enradigt block:

// helst för (var i = 0; i< 10; i += 1) { alert(i); }

Även för villkor:

// dålig om (true) varning (1); annars varning (2); // helst om (true) (alert (1);) else (alert (2);)

Öppna parentes position

Ofta uppstår frågan, var ska den öppna parentesen placeras - på samma linje eller på nästa?

If (true) (alert ("Message!");)

If (true) (alert ("Message");)

I detta exempel är parentesens position en fråga om preferenser. Men det finns fall där programmet kommer att bete sig olika beroende på parentesens position. Denna situation uppstår på grund av mekanismen för infogning av semikolon - JavaScript analyserar inte när du bestämmer dig för att inte avsluta raden korrekt och lägger till semikolon för dig. Detta beteende kan orsaka problem när funktionen returnerar ett bokstavligt objekt och den öppna parentesen är på nästa rad:

// varning: oväntad returfunktion func () (retur // följt av ett kodblock som aldrig kommer att köras (namn: "Batman"))

Om du förväntade dig att den här funktionen skulle returnera ett objekt med en namnegenskap blir du obehagligt överraskad. På grund av det underförstådda semikolon returnerar funktionen odefinierad. Den tidigare koden är likvärdig nästa block:

// varning: oväntad returfunktion func () (retur odefinierad; // följt av ett kodblock som aldrig kommer att köras (namn: "Batman"))

Funktion func () (retur (namn: "Batman");)

Var uppmärksam på semikolon. Precis som lockiga hängslen bör du alltid använda ett semikolon, även om det antyds av det JavaScript -kod... Detta inte bara disciplinerar och överensstämmer med en striktare metod för kodning, utan hjälper också till att undvika tvetydiga situationer, som i exemplet ovan.







2021 gtavrl.ru.