Cargo-Culting i JavaScript

Cargo-kult programmering er hva en programmerer gjør når han eller hun ikke kjenner et bestemt språk eller paradigme godt nok, og så ender opp med å skrive redundant og muligens skadelig kode. Den reagerer på hodet ganske ofte i JavaScript-området. I denne artikkelen undersøker jeg begrepet cargo-programmering og steder for å passe på det i JavaScript.

Dogmatiske regler overflater og sprer seg, til de regnes som normen.

Cargo-kultivering er noen ganger definert som "ekstrem overholdelse av skjemaet i stedet for innhold." Skjemaet, i programmering, er syntaxen, paradigmene, stilene og mønstrene vi bruker. Innholdet er det abstrakte som du søker å representere gjennom koden din - selve stoffets innhold. En person med manglende forståelse i et område er sannsynlig å kopiere andres form uten virkelig forståelse, og dermed deres innhold - deres program - kan lide.

Cargo-kultivering er nysgjerrig vanlig i JavaScript, sannsynligvis på grunn av den generelle, lave barrieren for oppføring i front-end-utviklingsverdenen. Du kan piske opp en HTML-side med litt JavaScript på sekunder. Som et resultat er det mange som blir tilstrekkelig dyktige i disse teknologiene for å føle seg komfortabel å skape og innføre regler for seg selv og andre. Til slutt kopierer andre nykommere disse reglene. Dogmatiske regler overflater og sprer seg, til de regnes som normen:

  • Bruk alltid strenge likestillingsoperatører
  • Bruk aldri eval
  • Bruk alltid en enkelt vardeklarasjon per omfang
  • Bruk alltid en IIFE - det "beskytter" deg

En regel fortsetter å spre seg til en programmerer bare bruker en bestemt teknikk på grunn av sin popularitet, i stedet for å vurdere hver enkelt brukstilstand uavhengig.


JavaScript Abuzz med semikoloner

Hvis du har hatt muligheten til å vitne til den vittige banter og retorikken til programvareutvikleren gjennom årene, har du oppdaget en tendens til å diskutere tilsynelatende små ting i store lengder. Ting som semikolon, komma, hvite rom eller krøllete brace.

Syntaks som semikolon eller hvitrom kan virke som rent element i form, ikke innhold. Men mange av disse subtile syntaksreglene kan ha betydelige effekter i JavaScript. Hvis du ikke forstår 'skjemaet', kan du ikke begynne å forstå innholdet.

Så i denne artikkelen vil vi identifisere hvilke områder av skjemaet i JavaScript ofte fraktkultes av - det vil si, kopiert uten å forstå.

Hvordan JavaScript kan virke ... et bilde fra Angus Crolls "The Politics Of JavaScript" presentasjon

undefined

Angus Croll, i en nylig presentasjon, med tittelen "The Politics Of JavaScript", fremhevet en av de vanligste delene av JS dogma som folk laster ut av:

hvis (type av myObject.foo === 'undefined') ...

Mesteparten av tiden, gjør en så langvarig sjekk for udefinert er meningsløst. Teknikken ble vanlig fordi folk kopierte andre mennesker, ikke på grunn av den faktiske verdien.

Selvfølgelig er det tidspunkter når:

 typeof x === 'undefined'

... er å foretrekke for:

x === udefinert

Men det er også tider når sistnevnte foretrekkes. En rask oversikt over alternativene:

 // Bestem om x er undefined: x === undefined typeof x == 'undefined' typeof x === 'undefined' x === tomrom 0 // Bestem om 'x' er udefinert ELLER null: x = = null x == undefined

Folk begynte å bruke typeof tilnærming fordi de beskytter seg mot:

  • En potensielt svart variabel (Ikke-typiske tilnærminger ville kaste TypeErrors)
  • Noen overskriver udefinert globalt eller i foreldreomfang. Noen miljøer lar deg overskrive udefinert til noe som helst ekte. Du må spørre deg selv: "Er det sannsynlig at noen overskrider udefinert, og skal skriptet mitt pander til slik silliness?"

Men mesteparten av tiden beskytter de seg mot å måtte bekymre seg. Det er en fangst-alt unngås å måtte vite detaljene. Å vite detaljene kan hjelpe deg selv. Hver karakter av koden din bør eksistere med en hensikt i tankene.

Den eneste gangen du trenger å bruke en typeof se etter udefinert er når du ser etter en variabel som kanskje ikke er deklarert, f.eks. sjekker for jQuery i det globale omfanget:

 hvis (type av jQuery! = 'undefined') // ... Bruk jQuery

Saken er, hvis jQuery gjør eksisterer, så kan vi være sikre på at det er et objekt - en "truthy" ting. Så dette ville være tilstrekkelig:

 // eller: hvis (window.jQuery) 

Den store strenge / ikke-strenge debatten

La oss ta noe veldig vanlig og betraktes generelt som gode råd, bare ved hjelp av streng likestilling:

 a === b

Streng likestilling sies å være god fordi den unngår tvetydighet. Det kontrollerer både verdien og typen, noe som betyr at vi ikke trenger å bekymre oss for implisitt tvang. Med strengt likestilling må vi imidlertid bekymre oss om det:

 1 == 1 // true - ok, det er bra 1 == "1" // true - hmm 1 == [1] // true - wat!?

Så det virker fornuftig råd for å unngå unøyaktig likestilling, ikke sant? Faktisk nei. Det er mange situasjoner der strenge likestilling skaper store mengder redundans, og ikke-streng likestilling er å foretrekke.

Når du vet, med 100% sikkerhet, at typene av begge operandene er de samme, kan du unngå behovet for streng likestilling. For eksempel vet jeg alltid at typeof operatør returnerer en streng, og min høyre operand er også en streng (f.eks. "Nummer"):

 // Med strenge likt typeof x === 'tall' // Med ikke-streng-like: typeof x == 'tall'

De er begge effektivt identiske. Jeg foreslår ikke nødvendigvis at vi forlater strenge like i dette tilfellet - jeg foreslår at vi forblir oppmerksomme på hva vi gjør slik at vi kan ta de beste valgene i hver situasjon.

Et annet ganske nyttig eksempel er når du vil vite om en verdi er heller null eller udefinert. Med streng likestilling kan du gjøre dette:

 hvis (verdi === udefinert || verdi === null) // ...

Med ikke-streng likestilling er det langt enklere:

 hvis (verdi == null) // ...

Det er ingen fangst her - det gjør akkurat det vi vil, bare, uten tvil, mindre synlig. Men hvis vi kjenner språket, så hva er problemet? Det er der i spesifikasjonen:

Sammenligningen x == y, hvor x og y er verdier, produserer ekte eller falsk. En slik sammenligning utføres som følger:

  • Hvis x er null og y er udefinert, returner du sant.
  • Hvis x er udefinert og y er null, returner du sant.

Hvis du skriver JavaScript med den hensikt at den leses, om ikke, av folk som kjenner JavaScript, vil jeg hevde at du ikke burde føle deg dårlig å dra nytte av implisitte språkregler, slik som dette.


hasOwnProperty

De hasOwnProperty Metoden brukes til å avgjøre om en eiendom er direkte eid av et objekt. Finnes det vanligvis i for i sløyfer for å sikre at du bare roter med direkte egenskaper og ikke arvede egenskaper.

 for (var jeg i objekt) if (object.hasOwnProperty (i)) // Vi kan gjøre ting med 'objekt [i]'

Det er viktig å merke seg at for i erklæringen vil bare gå gjennom enumarable egenskaper. Native arvede metoder, for eksempel, er ikke enumerable og så du trenger ikke å bekymre deg for dem uansett.

De hasOwnProperty sjekk er spesielt hindret deg i å berøre egenskaper som du eller noen tredjeparts skript har definert, dvs. når prototypen til objektet ditt har utallige egenskaper.

Hvis du vet at objektets prototype (eller prototypens prototype etc.) har ikke noen utallige egenskaper, da du trenger ikke å bekymre deg om å bruke hasOwnProperty i din for i sløyfer. Og hvis objektet ditt er initialisert, via ES5 Object.create (null), da vil du ikke engang kunne ringe hasOwnProperty direkte på objektet (ingen prototype betyr ingen arvelige innfødte metoder). Dette betyr at du bruker hasOwnProperty som standard i alle dine for i sløyfer kan faktisk bryte noen ganger.

En potensiell løsning for gjenstander med null prototyper er å bruke en lagret referanse til hasOwnProperty, som så:

 var hasOwnProperty = Object.prototype.hasOwnProperty; // Senere i koden din: for (var jeg i noenObject) hvis (hasOwnProperty.call (someObject, i)) // ...

Det vil fungere selv om objektet ikke har noen prototype (i tilfelle av Object.create (null)). Men selvfølgelig bør vi bare gjøre dette i utgangspunktet hvis vi vet at vi trenger det. Hvis du skriver et tredjeparts skript for et "fiendtlig" miljø, så ja, sjekk definitivt for tallrike arvede egenskaper. Ellers kan det ikke være nødvendig hele tiden.

Merk: IE9 og Safari 2.0 kompliserer saken videre når du prøver å identifisere tallrike egenskaper som allerede er definert som ikke-talbare. Det er verdt å sjekke ut en virkelig kryssbrowser forOwn loop implementering.

Å konkludere: din bruk av hasOwnProperty bør avhenge av at objektet sløyfes over. Det avhenger av hvilke forutsetninger du trygt kan gjøre. Blindbeskytter deg selv ved hjelp av hasOwnProperty vil ikke være tilstrekkelig i alle tilfeller. Vær også forsiktig med forskjellene i nettleseren.


Over-Parenthesising

En annen vanlig redundans som kryper inn i JS-koden er parentesen. Innenfor uttrykk brukes den til å tvinge spesifikk gruppering av subuttrykk. Uten dem, er du til nåde av operatørens forrang og assosiativiteter. For eksempel:

 A && B || C A && (B || C) (A && B) || C

En av dem er ikke som den andre. Parantesene styrker en bestemt gruppering, og mange foretrekker ekstra klarhet. I dette tilfellet har den logiske OG-operatøren en høyere prioritet enn den logiske OR-operatøren, noe som betyr at det er de første og siste linjene som er likeverdige. Den andre linjen er en helt annen logisk operasjon.

Høyere prioritet betyr at det vil skje før andre operasjoner i en rekke operasjoner.

For å unngå denne kompleksiteten velger utviklere ofte en "parentes policy" - der du fortsetter å legge til parentes til det er rikelig klart hvilke operasjoner som forekommer, både for deg og potensielle lesere av koden. Det kan hevdes at denne verbositet ender opp med å gjøre ting mindre klar skjønt.

Det er vanskelig for en leser noen ganger. Man må vurdere at noen bestemte parenteser kan ha blitt lagt til fordi:

  • Det var nødvendig å overstyre standardpreferanse / assosiativitet
  • For ingen funksjonell grunn i det hele tatt, bare for "beskyttelse" eller "klarhet"

Ta dette eksempelet:

 A && B? doFoo (): doBaz ()

Uten kjennskap til prioritetsregler for operatører, kan vi se to mulige operasjoner her:

 (A && B)? doFoo (): doBaz () A && (B? doFoo (): doBaz ())

I dette tilfellet er det det logiske OG som har høyere forrang, noe som betyr at det tilsvarende parenteserte uttrykket er:

 (A && B)? doFoo (): doBaz ()

Vi burde ikke føle seg forpliktet til å legge til disse parentesene i vår kode, skjønt. Det skjer implisitt. Når vi gjenkjenner at det skjer implisitt, er vi fri til å ignorere det og fokusere på selve programmet.

Det er selvsagt gyldige argumenter for å beholde parentesene hvor implisitt gruppering er uklart. Dette kommer virkelig ned til deg og hva du er komfortabel med. Jeg vil imidlertid be deg om å lære forrangene, og da kan du få fullmakt til å ta den beste ruten, avhengig av den spesifikke koden du har å gjøre med.


Objektnøkler

Det er ikke sjelden å se overflødige sitater i objektet litteratur:

 var data = 'dato': '2011-01-01', 'id': 3243, 'handling': 'UPDATE', 'relatert': '1253': 2, '3411': 3;

I tillegg til strenge, lar JavaScript deg bruke gyldige identifikasjonsnavn og -numre som objektets bokstavelige nøkler, slik at ovennevnte kan skrives om til:

 var data = dato: '2011-01-01', id: 3243, handling: 'UPDATE', relatert: 1253: 2, 3411: 3;

Noen ganger kan du foretrekke den ekstra konsistensen av å kunne bruke sitater, spesielt hvis et feltnavn er et reservert ord i JavaScript (som "klasse" eller "instanceof"). Og det er greit.

Å bruke sitater er ikke en dårlig ting. Men det er overflødig. Å vite at du ikke trenger å bruke dem, er halve kampen vunnet. Det er nå ditt valg å gjøre det du vil.


Komma plassering

Det er en stor mengde subjektiv preferanse når det gjelder tegnsetting i programmering. Sist, har JavaScript-verdenen opplevd retorikk og misnøye over kommaet.

Initialisering av et objekt i tradisjonelt idiomatisk JavaScript ser slik ut:

 var obj = a: 1, b: 2, c: 3;

Det er en alternativ tilnærming, som har vunnet fart:

 var obj = a: 1, b: 2, c: 3;

Den antatte fordelen med å plassere kommaene før hvert nøkkelverdierpar (bortsett fra det første) er at det betyr at du bare trenger å berøre en linje for å fjerne en eiendom. Ved å bruke den tradisjonelle tilnærmingen, trenger du å fjerne "c: 3"og deretter de kommende kommaene på linjen ovenfor. Men med kommas første tilnærming kan du bare fjerne", c: 3". Forutsetninger hevder at det er mindre sannsynlig at trailingkomma er mindre sannsynlig og også rydder opp kildekontrolldifferensene.

Motstandere sier imidlertid at denne tilnærmingen bare oppnår å bli kvitt det kommende komma-problemet ved å introdusere et nytt kommando-kommaproblem. Prøv å fjerne første linje, og du er igjen med et ledende komma på neste linje. Dette er faktisk betraktet som en god ting ved comma-første tiltalere, fordi et ledende komma umiddelbart kaster en SyntaxError. Et tilbakekalt komma kaster imidlertid ingenting, unntatt i IE6 og 7. Så hvis utvikleren ikke klarer å teste JS i disse versjonene av IE, kan de kommende kommaene ofte krype inn i produksjonskoden, noe som aldri er bra. Et ledende komma kast i alle miljøer, så er mindre sannsynlig å bli savnet.

Selvfølgelig kan du hevde at hele denne tingen er opptatt. Vi bør nok bruke linjer som JSLint eller Kinder JSHint. Da er vi fri til å bruke tegnsetting og hvite plassplassering som gir oss mest mening og våre kolleger.

La oss ikke engang komme i gang med kommas første stil i variabeldeklarasjoner ...

 var a = 1, b = 2, c = 3;

Du er koden for psykopater?

Vi bør forsøke å lære språkene vi bruker til et godt nok nivå at vi kan unngå lastkultur og overbeskyttende fangst-alle kodeteknikker. Og vi bør stole på at våre kolleger og andre utviklere gjør det samme.

Vi har også diskutert overgivelsen av cruft til fordel for å utnytte et språks idiosynkrasjoner og implisitte regler. For noen skaper dette vedlikeholdsproblemer, spesielt hvis noen som er yngre i oppkjøpet av et bestemt språk nærmer seg koden. For eksempel, hva hvis de ikke vet om JavaScript er svakt kontra streng likestilling?

På temaet vedlikeholdsevne blir vi påminnet av dette berømte sitatet:

Alltid kode som om personen som ender opp med å opprettholde koden din, er en voldelig psykopat som vet hvor du bor.

Jeg vet ikke om det er virkelig godt råd. Selv tatt metaforisk, antyder det en mistillid til den fiktive vedlikehaverens kompetanse - og behovet for å bekymre seg for deres forståelse over alt annet. Jeg vil heller skrive kode i kunnskap om at det vil bli tatt vare på av folk som kjenner sine ting. Så som en mulig motsigelse eller til og med et tillegg til det sitatet, tilbyr jeg:

Alltid kode som om personen som ender opp med å opprettholde koden din, er kunnskapsrik om språket og dets konstruksjoner og søker å få forståelse for problemdomenet ved å lese koden din.

Selv om dette kanskje ikke alltid er sant, bør vi søke etter at det er slik. Vi bør forsøke å sikre at folk som jobber med en bestemt teknologi, har tilstrekkelig forståelse for å gjøre det. Den lærde cargo-culter sier:

Hvis jeg for alltid pander til et lavere nivå av forståelse i koden min - treading mykt - strengt overholdt konvensjoner og stil guider og ting jeg ser "eksperter" gjør, så kan jeg aldri fremme min egen forståelse eller dra nytte av en språk i all sin raritet og skjønnhet. Jeg er lykkelig og villiggjort i denne verden av regler og absolutter, men for å gå videre må jeg gå ut av denne verden og omfavne høyere forståelse.