Cargo-Culting în JavaScript

Cargo-cult de programare este ceea ce un programator face atunci când el sau ea nu cunosc o anumită limbă sau paradigmă destul de bine, și astfel ajunge la scrierea redundante și, eventual, codul dăunătoare. Își ridică capul destul de des în țara JavaScript. În acest articol, analizez conceptul de programare a cultului de marfă și locuri de urmărit în JavaScript.

Regulile dogmatice se întind și se răspândesc, până când sunt considerate norme.

Cargo-cultingul este uneori definit ca "aderența extremă la formă în loc de conținut". Forma, în programare, fiind sintaxa, paradigmele, stilurile și modelele pe care le folosim. Conținutul fiind lucrarea abstractă pe care încercați să o reprezentați prin codul dvs. - însăși substanța programului dvs. O persoană cu lipsă de înțelegere într-o zonă este probabil să copieze forma celorlalți fără o înțelegere reală și astfel conținutul lor - programul lor - poate suferi.

Cargo-culting este curios de obișnuit în JavaScript, probabil din cauza barierei generale scăzute la intrarea în lumea dezvoltării front-end. Puteți să biciuiți o pagină HTML cu un pic de JavaScript în câteva secunde. Ca rezultat, există mulți oameni care sunt suficient de competenți în aceste tehnologii pentru a se simți confortabil în crearea și impunerea de reguli asupra lor și a altora. În cele din urmă, alți nou-veniți copiază aceste reguli. Regulile dogmatice se întind și se răspândesc, până când sunt considerate norme:

  • Utilizați întotdeauna operatori de egalitate stricți
  • Nu folosiți niciodată eval
  • Utilizați întotdeauna o singură declarație var pe domeniu
  • Utilizați întotdeauna un IIFE - vă "protejează"

O regulă continuă să se răspândească până când un programator folosește numai o anumită tehnică datorită popularității sale, în loc să considere fiecare caz specific de utilizare independent.


JavaScript Abuzz cu punct și virgulă

Dacă ați avut ocazia să vă ascultați de-a lungul anilor vrăjitorul și retorica impresionantă a dezvoltatorului de software, veți observa tendința de a discuta lucruri aparent minuscule. Lucruri cum ar fi punct și virgulă, virgulă, spațiu alb sau brățară cretată.

Sintaxa ca punct și virgulă sau spațiu alb poate părea a fi elemente de formă, nu de conținut. Dar multe dintre aceste reguli sintactice subtile pot avea efecte semnificative în JavaScript. Dacă nu înțelegeți "forma", atunci nu puteți începe să înțelegeți "conținutul".

Deci, în acest articol, vom identifica ce zone de formă din JavaScript sunt adesea încărcate de marfă - adică copiate fără înțelegere.

Cum poate părea JavaScript ... o imagine din prezentarea "Politica JavaScript" a lui Angus Croll

Nedefinit

Angus Croll, într-o prezentare recentă, intitulată "Politica JavaScript", a subliniat una dintre cele mai frecvente piese de dogma JS pe care oamenii le încarcă pe:

dacă (typeof myObject.foo === 'undefined') ...

De cele mai multe ori, efectuând o astfel de verificare lungă pentru nedefinit este inutilă. Tehnica a devenit obișnuită deoarece oamenii copiau alți oameni, nu din cauza valorii lor reale.

Desigur, sunt momente când:

 tipof x === 'undefined'

... este preferabil să:

x === nedefinit

Dar, în mod egal, există momente în care acesta este preferat. O prezentare rapidă a opțiunilor:

 // determina dacă 'x' este nedefinit: x === undefined typeof x == 'undefined' typeof x === 'undefined' x === void 0 // determina dacă 'x' este nedefinit SAU null: x = = null x == undefined

Oamenii au început să folosească tip de pentru că se proteja împotriva:

  • O variabilă potențial nedeclarată (abordările non-tip ar arunca TypeErrors)
  • Cineva a înlocuit nedefinit la nivel global sau într-un domeniu de bază. Unele medii vă permit să suprascrieți nedefinit la ceva asemănător Adevărat. Trebuie să vă întrebați:Este probabil ca cineva să-l înlocuiască pe nedefinit și dacă scenariul meu ar trebui să se supună unei asemenea umilințe?"

Dar, de cele mai multe ori, se protejează de a nu-și face griji. Este o evitare totală de a cunoaște detaliile. Cunoașterea detaliilor vă poate ajuta totuși. Fiecare caracter al codului dvs. ar trebui să existe cu un scop în minte.

Singura dată când ar trebui să folosiți a tip de verifica pentru nedefinit este atunci când verificați o variabilă care s-ar putea să nu fi fost declarată, de ex. verificarea jQuery în domeniul global:

 dacă (tipul jQuery! = 'undefined') // ... Utilizați jQuery

Chestia este dacă jQuery face există, atunci putem fi siguri că este un obiect - un lucru "trist". Deci, acest lucru ar fi suficient:

 // sau: if (window.jQuery) 

Marea dezbatere strictă / nestricătoare

Să luăm ceva foarte comun și, în general, considerăm un sfat bun, folosind doar egalitatea strictă:

 a === b

Se spune că egalitatea strictă este bună deoarece evită ambiguitatea. Verifică atât valoarea, cât și tipul, ceea ce înseamnă că nu trebuie să ne facem griji cu privire la coerciția implicită. Cu non-egalitate strictă, trebuie să ne îngrijorăm:

 1 == 1 // true - bine, asta e bun 1 == "1" // true - hmm 1 == [1] // true - wat!?

Așadar, ar părea sfat util să evitați în întregime egalitatea fără stricte, nu? De fapt nu. Există multe situații în care egalitatea strictă creează cantități mari de redundanță și este preferabilă egalitatea nestrictă.

Când știți, cu certitudine de 100%, că tipurile ambelor operanzi sunt aceleași, puteți evita necesitatea unei egalități stricte. De exemplu, întotdeauna știu că tip de operatorul returnează un șir, iar operandul meu de dreapta este, de asemenea, un șir (de ex. "număr"):

 // Cu strict egal typeof x === 'number' // Cu non-strict egal: typeof x == 'number'

Ambele sunt în mod identic identice. Nu spun neapărat că abandonăm egalitatea strictă în acest caz - sugerez să rămânem conștienți de ceea ce facem astfel încât să putem face cele mai bune alegeri în funcție de fiecare situație.

Un alt exemplu destul de util este atunci când doriți să aflați dacă o valoare este una nul sau nedefinit. Cu egalitate strictă, puteți face acest lucru:

 dacă (valoare === undefined || value === null) // ...

Cu egalitate nestrictă, este mult mai simplu:

 dacă (valoare == null) // ...

Nu există nici o captură aici - face exact ceea ce vrem, numai, arguably, mai puțin vizibil. Dar, dacă știm limba, atunci care este problema? Este exact acolo în spec:

Comparația x == y, Unde X și y sunt valori, produce Adevărat sau fals. O astfel de comparație se realizează după cum urmează:

  • Dacă x este nulă și y este nedefinită, returnează true.
  • Dacă x este nedefinit și y este nul, întoarce-te adevărat.

Dacă scrieți JavaScript cu intenția de a fi citit, dacă este deloc, de către persoane care știu JavaScript, atunci aș susține că nu ar trebui să te simți prost profitând de regulile limbajului implicit, cum ar fi acest lucru.


hasOwnProperty

hasOwnProperty metoda este utilizată pentru a determina dacă o proprietate este în proprietatea directă a unui obiect. Se găsește frecvent în pentru ... in buclele pentru a vă asigura că vă confruntați numai cu proprietăți directe și nu cu proprietăți moștenite.

 pentru (var i în obiect) if (object.hasOwnProperty (i)) // Putem face chestii cu 'obiect [i]'

Este important să rețineți că pentru-in afirmația va trece prin proprietăți enumarabile. Metodele native mostenite, de exemplu, nu sunt enumerabile și deci nu trebuie să vă faceți griji în legătură cu acestea oricum.

hasOwnProperty verificarea vă împiedică în mod specific să atingeți proprietățile pe care le-ați definit tu sau un script terță parte, adică atunci când prototipul obiectului are proprietăți enumerabile.

Dacă știți că prototipul obiectului (sau prototipul prototipului său etc) nu are nici o proprietăți enumerabile, atunci nu trebuie să vă faceți griji despre utilizare hasOwnProperty în tine pentru-in bucle. Și dacă obiectul dvs. este inițializat, prin ES5 Object.create (null), atunci nici măcar nu vei putea să suni hasOwnProperty direct pe obiect (nici un prototip nu înseamnă metode native moștenite). Aceasta înseamnă că utilizarea hasOwnProperty în mod prestabilit în toate dvs. pentru-in buclele pot rupe de fapt uneori.

O soluție potențială pentru obiecte cu nul prototipuri este de a utiliza o referință salvată la hasOwnProperty, ca astfel:

 var hasOwnProperty = Object.prototype.hasOwnProperty; // Mai târziu în codul dvs.: pentru (var i în someObject) if (hasOwnProperty.call (someObject, i)) // ...

Aceasta va funcționa chiar dacă obiectul nu are prototip (în cazul lui Object.create (null)). Dar, bineînțeles, ar trebui să facem acest lucru numai dacă știm că avem nevoie de ea. Dacă scrieți un script de terță parte pentru un mediu "ostil", atunci da, verificați cu siguranță proprietățile enumerate moștenite. În caz contrar, este posibil să nu fie necesar tot timpul.

Notă: IE9 și Safari 2.0 complică problema mai mult atunci când încercați să identificați proprietăți enumerabile care sunt deja definite ca non-enumerable. Merită să verificați o implementare cu adevărat încrucișată pentru buclă.

În încheiere: folosirea dvs. de hasOwnProperty ar trebui să depindă de obiectul care este bucle. Depinde de ce presupuneri puteți face în siguranță. Protejați-vă orbiți-vă folosind hasOwnProperty nu va fi suficient în toate cazurile. Aveți grijă de diferențele de browser încrucișate.


Supra-Parenthesising

O altă redundanță obișnuită care se strecoară în codul JS este paranteza. În cadrul expresiilor, se folosește pentru a forța gruparea specifică a sub-expresiilor. Fără ele, vă aflați în mâinile precedentelor și asociativităților operatorului. De exemplu:

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

Unul dintre ele nu este ca celălalt. Parantezele forțează o grupare specifică și mulți oameni preferă claritatea suplimentară. În acest caz, operatorul logical AND are o prioritate mai mare decât operatorul logic OR, ceea ce înseamnă că este prima și ultima linie care sunt echivalente. A doua linie este o operație logică complet diferită.

Prioritatea superioară înseamnă că va avea loc înainte de alte operațiuni într-o serie de operațiuni.

Pentru a evita această complexitate, dezvoltatorii optează frecvent pentru o "politică a parantezelor" - în care continuați să adăugați paranteze, până când este foarte clar ce operații au loc, atât pentru dvs., cât și pentru potențialii cititori ai codului. Se poate argumenta că această verbozitate sfârșește prin a face lucrurile mai puțin clare deși.

Este uneori dificil pentru un cititor. Trebuie să considerăm că este posibil să fi fost adăugate paranteze date deoarece:

  • A fost necesar să se override prioritatea / asociativitatea implicită
  • Pentru nici un motiv funcțional deloc, doar pentru "protecție" sau "claritate"

Luați acest exemplu:

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

Fără cunoașterea regulilor de prioritate ale operatorului, putem vedea două operațiuni posibile aici:

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

În acest caz, logica AND este cea care are precedența superioară, ceea ce înseamnă că expresia paranteză echivalentă este:

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

Nu ar trebui să ne simțim obligați să adăugăm aceste paranteze în codul nostru. Se întâmplă implicit. Odată ce recunoaștem că se întâmplă implicit, suntem liberi să o ignorăm și să ne concentrăm asupra programului în sine.

Există, desigur, argumente valabile pentru a păstra parantezele unde gruparea implicită este neclară. Acest lucru vine cu adevărat la dvs. și la ce vă simțiți confortabil. V-aș implora, totuși, să învățați precedentele și apoi puteți fi pe deplin împuternicit să luați cea mai bună rută, în funcție de codul specific cu care aveți de-a face.


Taste pentru obiecte

Nu este rară să vezi citate redundante în literali de obiecte:

 var data = 'data': '2011-01-01', 'id': 3243, 'action': 'UPDATE', 'conexe': '1253': 2, '3411': 3;

În plus față de șiruri de caractere, JavaScript vă permite să utilizați nume și numere de identificare valide ca chei de literă obiect, astfel încât cele de mai sus ar putea fi re-scris la:

 var data = data: '2011-01-01', id: 3243, acțiune: 'UPDATE', legate de: 1253: 2, 3411: 3;

Uneori, puteți prefera consistența adăugată a utilizării citatelor, mai ales dacă un nume de domeniu se întâmplă să fie un cuvânt rezervat în JavaScript (cum ar fi "clasa" sau "instanța"). Și este bine.

Utilizarea citatelor nu este un lucru rău. Dar este redundant. Știind că nu trebuie să le folosești este jumătate din bătălia câștigată. Acum este alegerea ta să faci ceea ce vrei.


Comandamentul locului

Există o mare cantitate de preferință subiectivă atunci când vine vorba de plasarea punctuației în programare. Cel mai recent, lumea JavaScript a fost abuzz cu retorica și nemulțumire față de virgulă.

Inițializarea unui obiect în JavaScript tradițional idiomatic arată astfel:

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

Există o abordare alternativă, care a câștigat un impuls deși:

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

Beneficiul presupus al plasării virgulelor înaintea fiecărei perechi cheie-valoare (cu excepția primei) este că înseamnă că trebuie să atingi doar o singură linie pentru a elimina o proprietate. Folosind abordarea tradițională, va trebui să eliminați "c: 3"și apoi virgula de pe linia de mai sus. Dar cu abordarea prin virgulă-prima poți să elimini", c: 3"Susținătorii susțin că acest lucru face ca virgulele de urmărire să fie mai puțin probabile și, de asemenea, curăță diferențele de control al surselor.

Oponenții spun însă că această abordare nu reușește decât să scape de "problema" de virgulă prin introducerea unei noi probleme de vârf. Încercați să eliminați prima linie și aveți o virgulă pe linia următoare. Acest lucru este de fapt considerat un lucru bun de către suporterii în virgulă, pentru că o virgulă de vârf ar arunca imediat un SyntaxError. Totuși, o virgulă care se află în virgulă nu aruncă nimic, cu excepția celor din IE6 și 7. Deci, dacă dezvoltatorul nu reușește să-și testeze JS-ul în versiunile IE, atunci virgulele posibile se pot adânci în codul de producție, care nu este niciodată bun. O virgulă care duce în toate mediile, este mai puțin probabil să fie ratată.

Bineînțeles, ați putea susține că întregul lucru este neclar. Probabil ar trebui să folosim linters ca JSLint sau JSHint. Apoi, suntem liberi să folosim destinația de punctuație și de plasare a spațiului alb care face cel mai mult sens pentru noi și colegii noștri.

Să nu începem chiar cu stilul virgulei în declarațiile variabile ...

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

Tu vei fi codul pentru psihopati?

Ar trebui să ne străduim să învățăm limbile pe care le folosim la un nivel suficient de bun încât să putem evita tehnicile de codificare a încărcăturii și culturării. Și ar trebui să avem încredere în colegii noștri și alți dezvoltatori să facă același lucru.

Am discutat, de asemenea, abandonarea cruftului în favoarea folosirii idiosincrațiilor unei limbi și a regulilor implicite. Pentru unii, acest lucru creează probleme de mentenabilitate, mai ales dacă cineva mai junior în achiziționarea unui anumit limbaj se apropie de cod. De exemplu, dacă nu știu despre slaba sau strictă egalitate a JavaScript-ului?

Pe tema mentenabilității, ne amintim de acest citat celebru:

Întotdeauna codați ca și cum persoana care vă sfătuiește să vă mențină codul este un psihopat violent care știe unde locuiți.

Nu știu dacă este cu adevărat un sfat bun. Chiar luată metaforic, sugerează o neîncredere față de competența fictivului de întreținere - și necesitatea de a vă îngrijora înțelegerea mai presus de orice. Mai degrabă aș scrie un cod în cunoașterea faptului că vor fi luate grijă de oamenii care știu lucrurile. Ca o posibilă contradicție sau chiar un addendum la acel citat, vă ofer:

Codați întotdeauna ca și cum persoana care sfârșește prin administrarea codului dvs. are cunoștință despre limbaj și construcțiile sale și încearcă să înțeleagă domeniul problemei prin citirea codului.

Deși acest lucru nu este întotdeauna adevărat, ar trebui să căutăm să fie așa. Trebuie să ne străduim să ne asigurăm că oamenii care lucrează la o anumită tehnologie au o înțelegere suficientă pentru a face acest lucru. Învățătorul de mărfuri învățat spune:

Dacă voi trăi pentru totdeauna la un nivel inferior al înțelegerii în codul meu - călcând ușor - respectând cu strictețe convențiile și ghidurile de stil și lucrurile pe care le văd "experții", atunci niciodată nu pot avansa înțelegerea mea, nici nu pot profita de limbă în toată ciudățenia și frumusețea ei. Sunt fericit și fericit stabilit în această lume a regulilor și absolutului, dar pentru a avansa, trebuie să părăsesc această lume și să îmi accept o înțelegere mai înaltă.

Cod