De kunst van het gooien van JavaScript-fouten

Toen ik jonger was, was het meest verwarrende deel van programmeertalen het vermogen om fouten te maken. Mijn eerste reactie op de throw operator in Java was:"Nou, dat is dom, waarom zou je ooit willen veroorzaken een foutmelding?" Fouten waren de vijand voor mij, iets wat ik probeerde te vermijden, dus de mogelijkheid om een ​​fout te veroorzaken leek een nutteloos en gevaarlijk aspect van de taal. Ik vond het dom om dezelfde operator in JavaScript op te nemen, een taal die mensen in de eerste plaats gewoon niet begrepen. Nu met veel ervaring onder mijn riem, ben ik een grote fan van het gooien van mijn eigen fouten. Als u dit op de juiste manier doet, kan dit leiden tot eenvoudiger debuggen en code-onderhoud.

Bij het programmeren treedt een fout op wanneer er iets onverwachts gebeurt. Misschien is de onjuiste waarde doorgegeven aan een functie of had een wiskundige bewerking een ongeldige operand. Programmeertalen definiëren een basisset van regels die, wanneer hiervan wordt afgeweken, resulteren in fouten, zodat de ontwikkelaar de code kan repareren. Foutopsporing zou bijna onmogelijk zijn als er geen fouten werden gegenereerd en aan u werden gerapporteerd. Als alles stil zou mislukken, zou het lang duren voordat je merkt dat er een probleem was, laat staan ​​het isoleren en oplossen. Fouten zijn de vrienden van ontwikkelaars, geen vijanden.

Het probleem met fouten is dat ze de neiging hebben om op onverwachte plaatsen en op onverwachte tijdstippen op te duiken. Om het nog erger te maken, zijn de standaardfoutmeldingen meestal te beknopt om echt uit te leggen wat er mis is gegaan. JavaScript-foutmeldingen zijn notoir weinig informatief en cryptisch (vooral in Internet Explorer), wat het probleem alleen maar verergert. Stel je voor dat er een fout opdook met een bericht dat zei:"deze functie is mislukt omdat dit is gebeurd." Onmiddellijk wordt uw foutopsporingstaak eenvoudiger. Dit is het voordeel van het gooien van je eigen fouten.

Het helpt om fouten te zien als ingebouwde faalgevallen. Het is altijd gemakkelijker om een ​​storing op een bepaald punt in de code te plannen dan om overal te anticiperen op een storing. Dit is een veel voorkomende praktijk in productontwerp, niet alleen in code. Auto's zijn gebouwd met kreukelzones, delen van het frame die zijn ontworpen om op een voorspelbare manier in te klappen bij een botsing. Door te weten hoe het frame zal reageren bij een crash en welke onderdelen zullen falen, kunnen de fabrikanten de veiligheid van de passagiers garanderen. Je code kan op dezelfde manier worden opgebouwd.

Hoewel JavaScript de afgelopen jaren een lange weg heeft afgelegd, hebben JavaScript-ontwikkelaars nog steeds veel minder tools om te helpen bij het opsporen van fouten dan ontwikkelaars van andere talen. Het gooien van fouten in uw JavaScript is aantoonbaar waardevoller dan in welke andere taal dan ook vanwege de moeilijkheden rond debuggen. Je kunt een gooien met de throw operator en het verstrekken van een object om te werpen. Elk type object kan worden gegooid, echter een Error object is het meest typisch om te gebruiken:

throw new Error("Something bad happened.")

Wanneer je op deze manier een fout genereert, en de fout wordt niet opgevangen via een try-catch statement, zal de browser de fouttekst op de gebruikelijke manier van de browser weergeven. Voor Internet Explorer betekent dit dat een klein pictogram in de linkerbenedenhoek van de browser wordt weergegeven en dat er een dialoogvenster met de fouttekst wordt weergegeven wanneer op dat pictogram wordt gedubbelklikt; Firefox met Firebug geïnstalleerd zal de fout in de console tonen; Safari en Chrome geven de fout door aan de Web Inspector; Opera toont de fout in de Error Console. Met andere woorden, het wordt op dezelfde manier behandeld als een fout die je niet hebt gemaakt.

Het verschil is dat u de exacte tekst kunt opgeven die door de browser moet worden weergegeven. In plaats van alleen regel- en kolomnummers, kunt u alle informatie opnemen die u nodig heeft om het probleem met succes op te lossen. Ik raad u meestal aan om altijd de functienaam in het foutbericht op te nemen, evenals de reden waarom de functie is mislukt. Overweeg de volgende functie:

function addClass(element, className){
    element.className += " " + className;
}

Het doel van deze functie is om een ​​nieuwe CSS-klasse toe te voegen aan het gegeven element (een veelgebruikte methode in JavaScript-bibliotheken). Maar wat gebeurt er als element is null ? U krijgt een cryptische foutmelding zoals 'object verwacht'. Vervolgens moet u naar de uitvoeringsstapel kijken (als uw browser dit ondersteunt) om de oorzaak van het probleem daadwerkelijk te lokaliseren. Debuggen wordt veel gemakkelijker door een fout te genereren:

function addClass(element, className){
    if (element != null && typeof element.className == "string"){
        element.className += " " + className;
    } else {
        throw new Error("addClass(): First arg must be a DOM element.");
    }
}

Discussies over het nauwkeurig detecteren of een object een DOM-element is of niet terzijde, deze methode biedt nu betere berichten wanneer het faalt vanwege een ongeldige element argument. Het zien van zo'n uitgebreid bericht in uw foutconsole leidt u onmiddellijk naar de bron van het probleem. Ik zie het gooien van fouten graag als het achterlaten van post-it-notities voor mezelf over waarom iets is mislukt.

Begrijpen hoe u fouten kunt maken, is slechts een deel van de vergelijking; begrijpen wanneer om fouten te gooien is de andere. Omdat JavaScript geen type- of argumentcontrole heeft, gaan veel ontwikkelaars er ten onrechte van uit dat ze dat voor elke functie zouden moeten implementeren. Dit is onpraktisch en kan de algehele prestaties van het script negatief beïnvloeden. De sleutel is om delen van de code te identificeren die waarschijnlijk op een bepaalde manier zullen falen en daar alleen fouten te veroorzaken. Kortom, gooi alleen fouten waar fouten al voorkomen.

Als een functie alleen wordt aangeroepen door bekende entiteiten, is foutcontrole waarschijnlijk niet nodig (dit is het geval bij privéfuncties); als je niet alle plaatsen kunt identificeren waar een functie van tevoren wordt aangeroepen, dan heb je waarschijnlijk wat foutcontrole nodig en heb je nog meer kans om je eigen fouten te maken. De beste plaats voor het gooien van fouten is in hulpprogramma-functies, die functies die een algemeen onderdeel zijn van de scriptomgeving en die op een willekeurig aantal plaatsen kunnen worden gebruikt. Dit is precies het geval met JavaScript-bibliotheken.

Alle JavaScript-bibliotheken zouden fouten uit hun openbare interfaces moeten genereren voor bekende foutcondities. YUI/jQuery/Dojo/etc. kan onmogelijk anticiperen wanneer en waar u hun functies zult bellen. Het is hun taak om je te vertellen wanneer je domme dingen doet. Waarom? Omdat je niet in hun code hoeft te debuggen om erachter te komen wat er mis is gegaan. De call-stack voor een fout moet eindigen in de interface van de bibliotheek, niet dieper. Er is niets erger dan een fout te zien die 12 functies diep in een bibliotheek bevat; bibliotheekontwikkelaars hebben de verantwoordelijkheid om dit te voorkomen.

Dit geldt ook voor privé JavaScript-bibliotheken. Veel webapplicaties hebben hun eigen propriëtaire JavaScript-bibliotheken die zijn gebouwd met of in plaats van de bekende openbare opties. Het doel van bibliotheken is om het leven van ontwikkelaars gemakkelijker te maken, en dit wordt gedaan door een abstractie te bieden weg van de vuile implementatiedetails. Het weggooien van fouten helpt om die vuile implementatiedetails veilig verborgen te houden voor ontwikkelaars.

JavaScript biedt ook een try-catch statement dat gegenereerde fouten kan onderscheppen voordat ze door de browser worden afgehandeld. Doorgaans hebben ontwikkelaars moeite om te onderscheiden of het gepast is om een ​​fout te genereren of om er een op te vangen met behulp van try-catch . Fouten mogen alleen in het diepste deel van de applicatie-stack worden gegooid, wat, zoals eerder besproken, meestal JavaScript-bibliotheken betekent. Elke code die toepassingsspecifieke logica verwerkt, moet over foutafhandelingsmogelijkheden beschikken en moet daarom fouten opvangen die worden gegenereerd door de componenten op een lager niveau.

Toepassingslogica weet altijd waarom het een bepaalde functie aanroept en is daarom het meest geschikt voor het afhandelen van de fout. Het is belangrijk om te vermelden dat je nooit een try-catch . mag hebben statement met een lege catch clausule; je moet altijd op de een of andere manier met fouten omgaan. Dit kan in ontwikkeling versus productie anders zijn, maar het moet gebeuren. Als er een fout optreedt, mag het antwoord nooit zijn om het simpelweg in een try-catch te wikkelen en laat het zo zijn - dit maskeert een fout in plaats van deze op te lossen.

Fouten gooien in JavaScript is een kunst. Het kost tijd om uit te zoeken waar de juiste delen van uw code fouten zouden moeten veroorzaken. Als je dit eenmaal hebt ontdekt, zul je echter merken dat je tijd voor het opsporen van fouten zal afnemen en je tevredenheid over de code zal toenemen.