Huvudprinciper för upprätthållbar JavaScript

JavaScript är ett nyfiken språk. Det är lätt att skriva, men svårt att behärska. I slutet av denna artikel kommer du förhoppningsvis att förvandla din spaghettikod till en fem-rätters måltid, full av läsbar, underhållbar yumminess!


Varför är det så tufft?

Saken att komma ihåg, framför allt när man skriver JS-kod, är att det är ett dynamiskt språk. Det betyder att det finns mycket sätt att göra saker. Du behöver inte hantera starkt skrivna klasser, eller några av de mer komplexa funktionerna från språk, som C # och Java. Detta är både en välsignelse och en förbannelse.

"Hårdheten" av JavaScript är tydligt uppenbart när man överväger följande bild:

Den lilla liten boken till vänster är Douglas Crockfords MUST READ-bok, JavaScript: De bra delarna. Torkning bredvid den till höger är, JavaScript Den slutgiltiga guiden, av David Flanagan.

Medan båda dessa böcker är utmärkta läser, illustrerar The Good Parts att även om JavaScript har en massa saker i den, kan de bra delarna summeras i en betydligt kortare läsning. Så, om du letar efter en bra, snabb läsning, gå med The Good Parts - och läs den ett par gånger!

Detta ledde naturligtvis till många sömnlösa nätter för webbutvecklare.

Du kan läsa en artikel om JavaScript-historien här, men det är känt att Brandon Eich, 1995, anställdes av Netscape för att utforma ett språk. Det han kom fram till var det löst typade språket som vi känner till som JavaScript. Genom åren har den blivit "standardiserad" som ECMAscript, men genom alla webbläsarkrig har de olika webbläsarna implementerat dessa funktioner olika. Detta leder naturligtvis till många sömnlösa nätter för webbutvecklare. Detta problem, i kombination med det faktum att JavaScript ansågs vara mest tillämpligt för att manipulera bilder och utföra snabba bitar av validering, ledde JavaScript till, felaktigt, betraktas som ett hemskt språk.

Det är dags att fixa det! Medan ja, det finns många dåliga saker om JavaScript, när det används ordentligt kan det vara ett fantastiskt språk - och det är dynamiskt naturen kommer att växa på dig!


Gör det bättre

namnrymder

En av nedfallet av hur JavaScript implementeras är att det fungerar på toppen av ett global objekt. När det gäller webbläsare kommer detta att sluta vara fönster objekt. Så, när som helst som kodar så här finns det på en sida ...

 funktion doStuff () alert ('Jag gör saker');  funktion doMoreStuff () var images = document.images.length; console.log ("Det finns" + bilder + "på den här sidan");  göra saker(); doMoreStuff ();

Funktionerna göra saker och den doMoreStuff funktionerna är omedelbart tillgängliga för hela världen fönster objekt.

Det betyder att om någon kommer och försöker skriva en funktion, som också kallas, göra saker, det kommer att bli en konflikt! Allt manus taggar tar i princip koden i dem och kör den mot fönster i den ordning som de hänvisas till i HTML. Som ett resultat, den andra personen att genomföra göra saker kommer att skriva över den första göra saker.

En vanlig teknik för att eliminera detta problem är att dra nytta av antingen självkörande anonyma funktioner eller namnområden. De objektorienterade folket som läser detta är troligen redan bekant med begreppet namnrymd, men grundtanken är att gruppera funktioner i olika områden för återanvändbarhet.

 var NS = NS || ; // "Om NS inte är definierat, gör det lika med ett tomt objekt" NS.Utils = NS.Utils || ; NS.Models = NS.Models || ; NS.Views = NS.Views || ;

Detta kommer att förhindra förorening av den globala namnrymden och hjälper till med läsbarhet för din ansökan. Nu definierar du bara funktioner i respektive namnrymd. En gemensamt definierad namnrymd är app, som hanterar resten av ansökan.

Designmönster och praxis

På varje språk finns en uppsättning designmönster. Addy Osmani säger ...

Designmönster är återanvändbara lösningar på vanliga problem i mjukvaruutveckling.

Det finns mycket, och när de används korrekt kan de i stor utsträckning påverka din applikations underhållbarhet. Addy skrev en bra JavaScript-designmönsterbok, kallad Essential Design Patterns. Absolut ge det en läsning!

Ett annat vanligt använt mönster är Återgivande modulmönster.

 NS.App = (funktion () // Initiera applikationen var init = funktion () NS.Utils.log ('Application initialized ...';; // Återvänd de offentliga motstående metoderna för App return init: i det ; ()); NS.App.init ();

Ovanför, an App funktionen definieras inom NS objekt. Inuti, en funktionsvariabel för i det definieras och returneras som en anonyma objektet bokstavligt. Observera att i slutet finns det den extra uppsättningen parentes: ());. Detta tvingar NS.App funktionen att automatiskt utföra och returnera. Nu kan du ringa NS.App.init () att initiera din app.

Den anonyma funktionen ovan är en bra metod i JavaScript, och kallas a Självkörande anonym funktion. Eftersom funktioner i JavaScript har sin egen räckvidd - det vill säga variabler som definieras inuti funktionerna är inte tillgängliga utanför dem - det gör anonyma funktioner användbara på flera sätt.

 // Wrap din kod i en SEAF (funktion (global) // Nu är några variabler du förklarar här inte tillgängliga utanför. Var somethingPrivate = 'du kan inte komma till mig!'; Global.somethingPublic = 'men du kan dock komma till mig! '; (fönster)); console.log (window.somethingPublic); // Detta fungerar ... console.log (somethingPrivate); // Fel

I det här exemplet, eftersom denna funktion utförs automatiskt kan du skicka fönster in i exekveringsdelen (fönster));, och det kommer att bli tillgängligt som global inuti den anonyma funktionen. Denna praxis begränsar de globala variablerna på fönster objekt och hjälper till att förhindra namnkollisioner.

Nu kan du börja använda SEAF i andra områden i din ansökan för att få koden att känna sig mer modulär. Detta gör det möjligt för din kod att återanvändas, och främjar god separation av problem.

Här är ett exempel på en potentiell användning för dessa idéer.

 (funktion ($) var welcomeMessage = 'Välkommen till den här applikationen!' NS.Views.WelcomeScreen = function () this.welcome = $ ('# welcome');; NS.Views.WelcomeScreen.prototype = showWelcome : funktion () this.welcome.html (welcomeMessage) .show ();; (jQuery)); $ (funktion () NS.App.init ();); // Ändra App.init ovan var init = funktion () NS.Utils.log ('Application initialized ...'); this.welcome = new NS.Views.WelcomeScreen (); this.welcome.showWelcome (); ;

Så ovanför finns det några olika saker som händer. för det första, jQuery överförs som ett argument till den anonyma funktionen. Detta säkerställer att $ är faktiskt jQuery inuti den anonyma funktionen.

Därefter finns en privat variabel, kallad välkomstmeddelande, och en funktion är tilldelad till NS.Views.WelcomeScreen. Inne i den här funktionen, this.welcome tilldelas en jQuery DOM-väljare. Detta cachar väljaren inuti welcomeScreen, så att jQuery inte behöver fråga DOM för det mer än en gång.

DOM-frågor kan vara minneskrävande, så se till att du cachar dem så mycket som möjligt.

Därefter sätter vi in ​​appen i det inom $ (Function () );, vilket är samma sak som att göra $ (Dokument) .ready ().

Slutligen lägger vi till en kod till appinitieraren. Detta håller din kod snygg och separerad, och det blir väldigt lätt att komma tillbaka till och ändra på en senare dag. Mer underhållbarhet!

Observer Mönster

Ett annat utmärkt mönster är Observer Mönstret - ibland kallat "Pubsub". Pubsub tillåter oss i princip att prenumerera på DOM-händelser, till exempel klick och mouseover. Å ena sidan är vi lyssnande till dessa händelser, och å andra sidan publicerar någonting händelserna - till exempel när webbläsaren publicerar (eller meddelar) att någon klickade på ett visst element. Det finns många bibliotek för pubsub, eftersom det är en kort bit av kod. Utför en snabb Google-sökning, och tusentals val gör sig tillgängliga. Ett solidt val är AmplifyJS: s implementering.

 // En datamodell för att hämta nyheter. NS.Models.News = (function () var newsUrl = '/ news /' // Hämta nyheter var getNews = function () $ .ajax (url: newsUrl typ: 'get', framgång: newsRetrieved) ;; var newsRetrieved = function (news) // Publicera hämtningen av news amplify.publish ('news-retrieved', nyheter);; return getNews: getNews; ());

Denna kod definierar en modell för att hämta nyheter från någon typ av tjänst. När nyheten har hämtats med AJAX, den newsRetrieved Metoden bränder, som passerar genom de hämtade nyheterna till Amplify, och publiceras på det nyhetssökta ämnet.

 (funktion () // Skapa en nyhetsvy. NS.Views.News = function () this.news = $ ('# news'); // Prenumerera på nyhetshämtningshändelsen. amplify.subscribe ('news- hämtad ", $ .proxy (this.showNews));; // Visa nyheten när den kommer NS.Views.News.prototype.showNews = function (news) var själv = detta; $ .each (artikel) self.append (artikel););; ());

Denna kod ovan är en vy för att visa de hämtade nyheterna. I Nyheter konstruktör, Amplify prenumererar på det nyhetshämtade ämnet. När det ämnet publiceras, showNews funktionen avfyras, följaktligen. Därefter bifogas nyheterna till DOM.

 // Ändra detta App.init ovan var init = funktion () NS.Utils.log ('Application initialized ...'); this.welcome = new NS.Views.WelcomeScreen (); this.welcome.showWelcome (); this.news = new NS.Views.News (); // Hämta nyheterna! NS.Models.News.getNews (); ;

Återigen, ändra i det funktionen från appen för att lägga till nyhetshämtningen ... och du är klar! Nu finns det separata delar av ansökan, som var och en är ansvarig för en enda åtgärd. Detta är känt som Principen om ensam ansvar.


Dokumentation och filer / Minifiering

En av nycklarna till underhållbar kod av något slag - inte bara JS - är dokumentation och kommentarer. Kommentarer kan vara oförmögen för nya utvecklare som kommer in i ett projekt - behöver förstå vad som händer i koden. "Varför skrev jag den ena raden igen?". Ett utmärkt verktyg för att generera dokumentation heter Docco. Det här är samma verktyg som genererar dokumentationen för Backbone.js webbplats. I grunden tar det dina kommentarer och placerar dem sida vid sida med din kod.

Det finns också verktyg, som JSDoc, som genererar en API-stil dokumentation, som beskriver varje klass i din kod.

En annan sak, som kan visa sig vara svårt när man påbörjar ett nytt projekt, försöker bestämma hur man bäst organiserar din kod. Ett sätt är att separera bitar av funktionalitet i separata mappar. Till exempel:

  • /app.js
  • /libs/jquery.js
  • /libs/jquery-ui.js
  • /users/user.js
  • /views/home.js

Denna struktur hjälper till att hålla delar av funktionallitet bortsett från varandra. Det finns självklart flera sätt att organisera kod, men allt som verkligen betyder är att bestämma en struktur ... och sedan rulla med den. Därefter kan du använda ett byggverktyg. Det finns många alternativ:

  • Grymta
  • Google Closure
  • JSMin
  • YUI kompressor

Dessa verktyg kommer att ta bort whitespace, ta bort kommentarer och kombinera alla angivna filer till en. Detta minskar filstorlekarna och HTTP-förfrågningarna för programmet. Ännu bättre betyder det att du kan hålla alla dina filer separerade under utveckling men kombinerad för produktion.


AMD

Asynkron modul Definition är ett annat sätt att skriva JavaScript-kod.

Asynkron modul Definition är ett annat sätt att skriva JavaScript-kod; Det delar upp all kod i separata moduler. AMD skapar ett standardmönster för att skriva dessa moduler för att ladda in kod asynkront.

Använder sig av manus taggar blockerar sidan, eftersom den laddas tills DOM är klar. Användning av något som AMD gör det möjligt för DOM att fortsätta ladda, medan skripten fortfarande laddas. I grund och botten är varje modul uppdelad i sin egen fil, och då finns en fil som slår av processen. Det mest populära genomförandet av AMD är RequireJS.

 // main.js kräver (['libs / jquery', 'app.js'], funktion ($, app) $ (funktion () app.init (););); // app.js define (['libs / jquery', 'views / home'], funktion ($, hem) home.showWelcome ();); // home.js define (['libs / jquery'], funktion ($) var home = function () this.home = $ ('# home');; home.prototype.showWelcome = function this.home.html ('Welcome!');; returnera nytt hem (););

I kodfältet ovan finns det a main.js fil, vilket är där processen börjar. Det första argumentet till fordra funktionen är en uppsättning beroenden. Dessa beroenden är en lista över filer som krävs för app.js. När de slutar ladda, oavsett vilken returmodul som helst returneras som ett argument till funktionen återuppringning till höger.

Då finns det app.js, vilket kräver jQuery, liksom en vy. Nästa, vyn, home.js, kräver bara jQuery. Den har en Hem fungera inom den och returnerar en förekomst av sig själv. I din ansökan lagras alla moduler i separata filer, vilket gör din ansökan mycket underhållbar.


Slutsats

Att hålla dina applikationer underhållbara är oerhört viktigt för utvecklingen. Det minskar buggar, och gör processen att fixa de som du hittar lättare.

"Vänner låt inte vänner skriva spagetti koden!"