Nøkkelprinsipper for vedlikehold av JavaScript

JavaScript er et nysgjerrig språk. Det er lett å skrive, men vanskelig å mestre. Ved slutten av denne artikkelen vil du forhåpentligvis forvandle spaghetti-koden til et fem retters måltid, fullt av lesbar, vedlikeholdsbar yumminess!


Hvorfor er det så tøft?

Ting å huske, fremfor alt når du skriver JS-kode, er at det er et dynamisk språk. Dette betyr at det er mye av måter å gjøre ting på. Du trenger ikke å håndtere sterkt skrevet klasser, eller noen av de mer komplekse funksjonene fra språk, som C # og Java. Dette er både en velsignelse og en forbannelse.

"Hardheten" til JavaScript er tydelig når du vurderer følgende bilde:

Den lille lille boken til venstre er Douglas Crockfords MUST READ-bok, JavaScript: De gode delene. Krysset ved siden av det, til høyre, er, JavaScript Den Definitive Guide, av David Flanagan.

Mens begge disse bøkene er gode leser, illustrerer The Good Parts at selv om JavaScript har mange ting i det, kan de gode delene oppsummeres i en betydelig kortere lesning. Så, hvis du leter etter en god, rask lesing, gå med The Good Parts - og les den et par ganger!

Dette førte selvsagt til mange søvnløse netter for webutviklere.

Du kan lese en artikkel om JavaScript-historien her, men kjennetegnet av det er at Brandon Eich, i 1995, ble ansatt av Netscape for å designe et språk. Det han kom opp med var det løst skrevet språket som vi kjenner som JavaScript. I løpet av årene ble det "standardisert" som ECMAscript, men gjennom alle nettleserkrigene implementerte de forskjellige nettleserne disse funksjonene på en annen måte. Dette fører selvsagt til mange søvnløse netter for webutviklere. Dette problemet, i kombinasjon med det faktum at JavaScript ble ansett som mest anvendelig for å manipulere bilder og utføre hurtige bits av validering, førte JavaScript til feil for å bli sett på som et forferdelig språk.

Det er på tide å fikse det! Mens ja, det er mange dårlige ting om JavaScript, når det brukes riktig, kan det være et fantastisk språk - og det er dynamisk natur vil vokse på deg!


Gjør det bedre

navnerom

En av nedgangene til hvordan JavaScript implementeres er at det opererer på toppen av en global gjenstand. I tilfelle av nettlesere, vil dette ende opp som værende vindu gjenstand. Så når som helst som kode som dette er til stede på en side ...

 funksjon doStuff () alert ('Jeg gjør ting');  funksjon doMoreStuff () var images = document.images.length; console.log ("Det er" + bilder + "på denne siden");  gjøre ting(); doMoreStuff ();

Funksjonene gjøre ting og doMoreStuff Funksjoner er umiddelbart tilgjengelige for det globale vindu gjenstand.

Dette betyr at hvis noen kommer sammen og forsøker å skrive en funksjon, som også kalles, gjøre ting, det vil bli en konflikt! Alle manus Tagger tar i utgangspunktet koden i dem, og kjører den mot vindu i den rekkefølgen de refereres til i HTML-koden. Som et resultat, den andre personen å implementere gjøre ting vil overskrive den første gjøre ting.

En vanlig metode for å eliminere dette problemet er å utnytte enten selvutførende anonyme funksjoner eller navneområder. De objektorienterte folkene som leser dette er sannsynligvis allerede kjent med begrepet navneområde, men grunnleggende ideen er å gruppere funksjoner i forskjellige områder for gjenbruk.

 var NS = NS || ; // "Hvis NS ikke er definert, gjør det lik et tomt objekt" NS.Utils = NS.Utils || ; NS.Models = NS.Models || ; NS.Views = NS.Views || ;

Dette vil forhindre forurensning av det globale navneområdet, og vil bidra til lesbarhet for søknaden din. Nå definerer du bare funksjoner i deres respektive navneområde. Et vanlig definert navneområde er app, som styrer resten av søknaden.

Design mønstre og praksis

På hvert språk finnes det et sett med designmønstre. Addy Osmani sier ...

Designmønstre er gjenbrukbare løsninger på vanlige problemer i programvareutforming.

Det er mange, og når de brukes riktig, kan de i stor grad påvirke søknadens vedlikeholdsevne. Addy skrev en flott JavaScript-designmønsterbok, kalt Essential Design Patterns. Absolutt gi det en lesning!

Et annet vanlig mønster er Å avsløre modulmønster.

 NS.App = (funksjon () // Initialiser applikasjonen var init = funksjon () NS.Utils.log ('Application initialized ...';;; // returnere de offentlige motstående metodene for App return init: i det ; ()); NS.App.init ();

Over, an app funksjon er definert i NS gjenstand. Inne, en funksjonsvariabel for i det er definert og returnert som en anonymt objekt bokstavelig. Legg merke til at på slutten er det det ekstra settet parentes: ());. Dette tvinger NS.App funksjon for å automatisk utføre og returnere. Nå kan du ringe NS.App.init () for å initialisere appen din.

Den anonyme funksjonen ovenfor er en god praksis i JavaScript, og refereres til som a Self-Executing Anonym Funksjon. Fordi funksjoner i JavaScript har sitt eget omfang - det vil si at variabler definert inne i funksjoner ikke er tilgjengelige utenfor dem - dette gjør anonyme funksjoner nyttige på flere måter.

 // Wrap koden din i en SEAF (funksjon (global) // Nå er noen variabler du erklærer her ute ikke tilgjengelig utenfor. Var somethingPrivate = 'du kan ikke komme til meg!'; Global.somethingPublic = 'men du kan imidlertid komme til meg! '; (vindu)); console.log (window.somethingPublic); // Dette fungerer ... console.log (somethingPrivate); // Feil

I dette eksemplet, fordi denne funksjonen utføres automatisk, kan du passere vindu inn i den henrettende delen (vindu));, og det vil bli gjort tilgjengelig som global inne i den anonyme funksjonen. Denne praksisen begrenser de globale variablene på vindu objekt, og vil hjelpe til med å forhindre navngivelse av kollisjoner.

Nå kan du begynne å bruke SEAF i andre områder av søknaden din for å gjøre koden føles mer modulær. Dette gjør at koden din kan brukes til nytte, og fremmer god adskillelse av bekymringer.

Her er et eksempel på en potensiell bruk for disse ideene.

 (funksjon ($) var welcomeMessage = 'Velkommen til dette programmet!' NS.Views.WelcomeScreen = function () this.welcome = $ ('# welcome');; NS.Views.WelcomeScreen.prototype = showWelcome : funksjon () this.welcome.html (welcomeMessage) .show ();; (jQuery)); $ (funksjon () NS.App.init ();); // Endre App.init ovenfor var init = funksjon () NS.Utils.log ('Application initialized ...'); this.welcome = new NS.Views.WelcomeScreen (); this.welcome.showWelcome (); ;

Så, over, er det noen forskjellige ting som skjer. for det første, jQuery er bestått som et argument for den anonyme funksjonen. Dette sikrer at $ er faktisk jQuery inne i den anonyme funksjonen.

Deretter er det en privat variabel, kalt velkomstmelding, og en funksjon er tilordnet til NS.Views.WelcomeScreen. Inne i denne funksjonen, this.welcome er tilordnet en jQuery DOM-velger. Dette caches selgeren inne i welcomeScreen, slik at jQuery ikke trenger å spørre DOM for det mer enn en gang.

DOM-spørringer kan være minneintensive, så vær sikker på at du cache dem så mye som mulig.

Deretter pakker vi inn appen i det innenfor $ (Funksjon () );, som er det samme som å gjøre $ (Document) .ready ().

Til slutt legger vi til noen kode i appinitieringen. Dette holder koden din fin og skilt, og det vil være betydelig enkelt å komme tilbake til og modifisere på en senere dag. Mer vedlikeholdsevne!

Observer Mønster

Et annet utmerket mønster er Observer Pattern - noen ganger referert til som "Pubsub." Pubsub lar oss i utgangspunktet abonnere på DOM-hendelser, for eksempel klikk og mus over. På den ene siden er vi lytting til disse hendelsene, og på den annen side publiserer noe disse hendelsene - for eksempel når nettleseren publiserer (eller kunngjør) at noen klikket på et bestemt element. Det er mange biblioteker for pubsub, da det er en kort bit av kode. Utfør et raskt Google-søk, og tusenvis av valg vil gjøre seg tilgjengelig. Et solidt valg er AmplifyJSs implementering.

 // En datamodell for å hente nyheter. NS.Models.News = (function () var newsUrl = '/ news /' // Hent nyheten var getNews = function () $ .ajax (url: newsUrl type: 'get', suksess: newsRetrieved) ;; var newsRetrieved = function (news) // Publiser henting av news amplify.publish ('news-retrieved', nyheter);; return getNews: getNews; ());

Denne koden definerer en modell for å hente nyheter fra en slags tjeneste. Når nyheten er hentet med AJAX, newsRetrieved metoden branner, passerer gjennom de hentede nyhetene til å forstørre, og er publisert på det nyhetsinnhentede emnet.

 (funksjon () // Opprett en nyhetsvisning. NS.Views.News = function () this.news = $ ('# news'); // Abonner på nyhetssøkningsbegivenheten. amplify.subscribe ('news- hentet ', $ .proxy (this.showNews));; // Vis nyheten når den kommer NS.Views.News.prototype.showNews = funksjon (nyheter) var selv = dette; $ .each (nyheter, funksjon (artikkel) self.append (artikkel););; ());

Denne koden ovenfor er en visning for visning av hentede nyheter. I Nyheter konstruktør, Amplify abonnerer på det nyhetsinnhentede emnet. Når det aktuelle emnet blir publisert, vil showNews funksjonen avfyres, tilsvarende. Deretter er nyheten lagt til DOM.

 // Modifiser dette App.init over var init = funksjon () NS.Utils.log ('Application initialized ...'); this.welcome = new NS.Views.WelcomeScreen (); this.welcome.showWelcome (); this.news = new NS.Views.News (); // Gå med nyheter! NS.Models.News.getNews (); ;

Igjen, endre i det Funksjon fra appen for å legge til nyhetsinnhentingen ... og du er ferdig! Nå er det separate deler av søknaden, som hver er ansvarlig for en enkelt handling. Dette er kjent som Enkelt ansvar prinsipp.


Dokumentasjon og filer / reduksjon

En av nøklene til vedlikeholdsbar kode av noe slag - ikke bare JS - er dokumentasjon og kommentarer. Kommentarer kan tjene til å være uutviklet for nye utviklere som kommer inn i et prosjekt - trenger å forstå hva som skjer i koden. "Hvorfor skrev jeg den ene linjen igjen?". Et utmerket verktøy for å generere dokumentasjon kalles Docco. Dette er det samme verktøyet som genererer dokumentasjonen for Backbone.js websiden. I utgangspunktet tar det dine kommentarer, og plasserer dem ved siden av koden din.

Det finnes også verktøy, som JSDoc, som genererer en API-stil dokumentasjon, som beskriver hver klasse i koden din.

En annen ting, som kan vise seg å være vanskelig når du starter et nytt prosjekt, prøver å bestemme hvordan du best kan organisere koden din. En måte er å skille stykker av funksjonalitet i separate mapper. For eksempel:

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

Denne strukturen bidrar til å holde deler av funksjonalitet bortsett fra hverandre. Det er selvfølgelig flere måter å organisere kode på, men alt som virkelig betyr noe, er å bestemme seg for en struktur ... og deretter rulle med den. Deretter kan du gjøre bruk av et bygge- og minifiseringsverktøy. Det er mange valg:

  • Grynte
  • Google Closure
  • JSMin
  • YUI kompressor

Disse verktøyene vil fjerne hvite mellomrom, fjerne kommentarer og kombinere alle spesifiserte filer til en. Dette reduserer filstørrelsene og HTTP-forespørsler for programmet. Enda bedre betyr dette at du kan holde alle filene dine skilt under utvikling, men kombinert for produksjon.


AMD

Asynkron modul definisjon er en annen måte å skrive JavaScript kode på.

Asynkron modul definisjon er en annen måte å skrive JavaScript kode på; Den deler alle koden i separate moduler. AMD lager et standardmønster for å skrive disse modulene for å laste inn kode asynkront.

Ved hjelp av manus Tagger blokkerer siden, som den laster inn til DOM er klar. Derfor vil bruk av noe som AMD tillate DOM å fortsette å laste, mens skriptene også lastes. I hovedsak er hver modul delt inn i sin egen fil, og så er det en fil som starter prosessen. Den mest populære implementeringen av AMD er RequireJS.

 // main.js krever (['libs / jquery', 'app.js'], funksjon ($, app) $ (funksjon () app.init (););); // app.js define (['libs / jquery', 'views / home'], funksjon ($, hjem) home.showWelcome ();); // home.js define (['libs / jquery'], funksjon ($) var home = function () this.home = $ ('# home');; home.prototype.showWelcome = funksjon this.home.html ('Welcome!');; returner nytt hjem (););

I kodebiten ovenfor er det a main.js fil, som er der prosessen begynner. Det første argumentet til krever funksjon er en rekke avhengigheter. Disse avhengighetene er en liste over filer som kreves for app.js. Når de er ferdige med å laste, blir det returnert modulen som et argument til funksjonen tilbakeringing til høyre.

Så er det app.js, som krever jQuery, samt en visning. Neste, visningen, home.js, krever bare jQuery. Den har en hjem fungere innenfor den, og returnerer en forekomst av seg selv. I din søknad lagres disse modulene i separate filer, noe som gjør søknaden din svært vedlikeholdsbar.


Konklusjon

Å holde søknadene vedlikeholdbare er ekstremt viktig for utviklingen. Det reduserer feil, og gjør prosessen med å fikse de som du finner enklere.

"Venner ikke la venner skrive spaghetti kode!"