Lataa kirjakurssit - Tarvitaanko kiharat aaltosulkeet JavaScriptin yksirivisissä operaatioissa?

Sanotaan, että kirjoitan

If(someVal) alert("True");

Sitten seuraava kehittäjä tulee ja sanoo: "Voi, minun täytyy tehdä jotain muuta", joten he kirjoittavat

If(someVal) alert("True"); hälytys("Tosi");

Nyt, kuten näet, "AndTrue" on aina totta, koska alkuperäinen kehittäjä ei käyttänyt kiharoita.

2018-12-04T00:00Z

Ehdottomasti kyllä

Unohda "Se on henkilökohtainen mieltymys", "koodi toimii hyvin", "se toimii hyvin minulle", "se on luettavampi" yada yada BS.

Argumentti: "Se on henkilökohtainen mieltymys"

Ei. Ellet ole ainoa henkilö, joka menee Marsiin, ei. Suurimman osan ajasta muut ihmiset lukevat/muokkaavat koodiasi. Missä tahansa vakavassa koodaustiimissä tämä olisi suositeltava tapa, joten se ei ole "henkilökohtainen mieltymys".

Argumentti: "koodi toimii hyvin"

Samoin spagettikoodi! Tarkoittaako tämä, että sellaisen luominen on ok?

Argumentti: "Se toimii hyvin minulle"

Olen nähnyt urani aikana niin monia bugeja, jotka on luotu tämän ongelman takia. Et luultavasti muista kuinka monta kertaa olet kommentoinut "DoSomething()" ja olet ymmälläsi, miksi "SomethingElse()" on nimeltään:

Jos (ehto) DoSomething(); Jotain muuta();

Jos (ehto) DoSomething(); Jotain enemmän();

Tässä esimerkki tosielämästä. Joku halusi ottaa kaiken kirjaamisen käyttöön, jotta he suorittaisivat etsi ja korvaa "Console.println" => //"Console.println" :

If (ehto) Console.println("jotain"); Jotain muuta();

Katso ongelma?

Vaikka ajattelet: "Tämä on niin triviaalia, etten koskaan tee sitä"; muista, että aina löytyy tiimin jäsen, jolla on heikommat ohjelmointitaidot kuin sinä (toivottavasti et ole joukkueen huonoin!)

Argumentti: "se on luettavampaa"

Jos olen oppinut jotain ohjelmoinnista, niin se on hyvin yksinkertaista. On hyvin yleistä, että tämä on:

Jos (ehto) DoSomething();

muuttuu seuraavaksi sen jälkeen, kun se on testattu eri selaimet/ ympäristöt / käyttötapaukset tai uusia ominaisuuksia lisätty:

If (a != null) if (ehto) DoSomething(); else DoSomethingElse(); TeeSomethingMore(); else if (b == null) hälytys("virhe b"); else alert("virhe a");

Ja vertaa tähän:

If (a != null) ( if (ehto) ( DoSomething(); ) else ( DoSomethingElse(); DoSomethingMore(); ) ) else if (b == null) ( hälytys("virhe b"); ) else ( hälytys("virhe a");

PS: Bonus pisteet mene sen puoleen, joka huomasi virheen yllä olevassa esimerkissä

2018-12-11T00:00Z

@Josh K:n mainitseman syyn lisäksi (joka koskee myös Javaa, C:tä jne.), yksi seuraavista erityisiä ongelmia JavaScriptissä on puolipisteen automaattinen lisääminen. Wikipedian esimerkistä:

Palautus a + b; // Palauttaa määrittelemättömän. Käsitelty: // paluu; // a + b;

Joten se voi myös antaa odottamattomia tuloksia, jos sitä käytetään seuraavasti:

Jos (x) palauttaa a + b;

Ei oikeastaan ​​ole paljon parempi kirjoittaa

Jos (x) ( palauttaa a + b; )

mutta ehkä tässä oleva virhe on hieman helpompi havaita(?)

2018-12-18T00:00Z

Kysymys koskee yhden rivin väitteitä. Monet esimerkit antavat kuitenkin syitä olla jättämättä kiharoita useille viivoille. On melko turvallista olla käyttämättä sulkuja samalla rivillä, jos se on haluamasi koodaustyyli.

Esimerkiksi kysymys kysyy, onko tämä normaalia:

If (ehto) -lause;

Hän ei kysy, onko kaikki hyvin:

If (ehto) -lause;

Mielestäni sulkeiden jättäminen on parempi, koska se tekee koodista luettavamman vähemmällä redundantilla syntaksilla.

Koodaustyylini on olla koskaan käyttämättä sulkeita, ellei koodi ole lohko. Älä koskaan käytä useita lauseita samalla rivillä (eroteltuna puolipisteillä). Minusta on helppo lukea ja siivota, eikä minulla ole koskaan ongelmia "jos"-lauseiden kanssa. Tämän seurauksena sulkujen käyttö yhdessä jos -ehto vaatii 3 riviä. Kuten tämä:

Jos (ehto) ( lausunto; )

Yhden rivin käyttö jos lause on parempi, koska se käyttää vähemmän pystysuoraa tilaa ja koodi on kompaktimpi.

En pakottaisi muita käyttämään tätä menetelmää, mutta se toimii minulle, enkä voisi olla enemmän eri mieltä annettujen esimerkkien kanssa siitä, kuinka sulut jätetään pois koodaus-/scoping-virheiden vuoksi.

2018-12-25T00:00Z

Ei

Tämä on täysin totta

If (ehto) alert ("Ehto täytetty!") else alert ("Ehto ei täytetty!")

Tätä samaa käytäntöä noudatetaan kaikissa C-syntaksityylisissä kielissä, joissa on sidonta. C, C++, Java, jopa PHP tukevat yksirivistä operaattoria ilman sulkuja. Sinun on ymmärrettävä, että säästät vain kaksi hahmoa, ja joillakin tyyleillä joillekin ihmisille et edes ylläpidä linjaa. Pidän parempana täyshousun tyylistä (kuten seuraava), joten se on yleensä hieman pidempi. Kompromissi toteutuu erittäin hyvin sillä, että sinulla on erittäin selkeä koodin luettavuus.

Jos (ehto) ( hälytys("Ehto täytetty!") ) else ( alert("Ehto ei täytetty!") )

2019-01-01T00:00Z

Ensimmäinen taso operaattorin sisennyksen on oltava yhtä suuri kuin aukkojen lukumäärä aaltosulkeet sen yli. (pois lukien lainausmerkit tai kommentit aaltosulkeista tai symboleista esikäsittelyohjeissa)

Muuten K&R olisi hyvä luetelmakohta. Korjataksesi heidän tyyliään, suosittelen lähettämistä lyhyesti yksinkertaiset operaattorit jos yhdellä rivillä.

Olen aina ollut yllättynyt JavaScript Ensinnäkin se on luultavasti kuin mikään muu laajalti käytetty kieli, joka tukee samanaikaisesti molempia paradigmoja: normaalia ja epänormaalia ohjelmointia. Ja jos lähes kaikki on luettu sopivista parhaista käytännöistä ja malleista, niin se ihmeellinen maailma, kuinka koodia ei tarvitse kirjoittaa, vaan voi, jää vain hieman raolleen.


Tässä artikkelissa analysoimme toista kaukaa haettua ongelmaa, joka vaatii anteeksiantamatonta normaalin ratkaisun rikkomista.


Edellinen tehtävä:

Formulaatio

Ota käyttöön koristelutoiminto, joka laskee läpäissyt toiminnon puhelut ja tarjoaa mahdollisuuden hakea kyseinen numero pyynnöstä. Päätöksessä kielletty käytä kiharoita ja yleisiä muuttujia.

Puhelulaskuri on vain tekosyy, koska siellä on console.count() . Asia on siinä, että funktiomme kerää dataa kutsuessaan wrapped-funktiota ja tarjoaa jonkinlaisen käyttöliittymän siihen pääsyä varten. Tämä voi olla kaikkien puhelun tulosten tallentaminen, lokien kerääminen tai jonkinlainen muistiinpano. Laskuri on vain primitiivinen ja ymmärrettävä kaikille.


Koko vaikeus piilee epänormaalissa rajoituksessa. Et voi käyttää kiharaa aaltosulkeet, mikä tarkoittaa, että sinun on harkittava uudelleen jokapäiväisiä käytäntöjä ja tavallista syntaksia.

Tavallinen ratkaisu

Ensin sinun on valittava aloituspiste. Yleensä, jos kieli tai sen laajennus ei tarjoa vaadittu toiminto koristelu, toteutamme tietyn säiliön itse: kääritty toiminto, kertyneet tiedot ja käyttöliittymä siihen pääsyä varten. Usein tämä on luokka:


class CountFunction ( konstruktori(f) ( this.calls = 0; this.f = f; ) invoke() ( this.calls += 1; return this.f(...arguments); ) ) const csum = new CountFunction ((x, y) => x + y); csum.invoke(3, 7); // 10 csum.invoke(9, 6); // 15 csum.calls; // 2

Tämä ei heti sovi meille, koska:

  1. JavaScriptissä et voi toteuttaa yksityistä omaisuutta tällä tavalla: voimme joko lukea puhelut esimerkki (jota me tarvitsemme) ja kirjoita siihen arvo ulkopuolelta (jota emme tarvitse). Tietysti voimme käyttää sulkemista konstruktorissa, mutta mitä järkeä luokassa sitten on? Toistaiseksi olisin varovainen käyttämästä uusia yksityisiä kenttiä ilman Babel 7:ää.
  2. Kieli tuettu toiminnallinen paradigma ja luoda ilmentymä kautta Uusi ei näytä olevan täällä paras ratkaisu. On mukavampaa kirjoittaa funktio, joka palauttaa toisen funktion. Joo!
  3. Lopuksi syntaksi Luokkailmoitus Ja MethodDefinition ei anna meidän päästä eroon kaikista kiharoista olkaimista huolimatta kuinka paljon haluaisimme.

Mutta meillä on loistava, joka toteuttaa yksityisyyden sulkemisen avulla:


function count(f) ( anna kutsujen = 0; return ( invoke: function() ( kutsuu += 1; return f(...argumentit); ), getCalls: function() ( paluu kutsut; ) ); ) const csum = count((x, y) => x + y); csum.invoke(3, 7); // 10 csum.invoke(9, 6); // 15 csum.getCalls(); // 2

Tämän kanssa voi jo työskennellä.

Mielenkiintoinen ratkaisu

Mihin tässä muuten käytetään kiharaisia ​​housuja? Nämä ovat 4 eri tapausta:

  1. Kehon määritelmä laskentafunktiot (Toimintoilmoitus)
  2. Palautetun kohteen alustaminen
  3. Funktion invoke ( FunctionExpression) kahdella lausekkeella
  4. Funktion rungon getCalls( FunctionExpression) yhdellä lauseella

Aloitetaan toinen kohta. Itse asiassa meidän ei tarvitse palata uusi kohde, mutta vaikeuttaa lopullisen funktion kutsumista kautta vedota. Voimme hyödyntää sitä tosiasiaa, että JavaScriptin funktio on objekti, mikä tarkoittaa, että se voi sisältää omansa mukautettuja kenttiä ja menetelmät. Luodaan palautusfunktiomme df ja lisää siihen menetelmä getCalls, joille sulkemisen kautta on pääsy puhelut kuten ennen:


funktio count(f) ( anna kutsujen = 0; funktio df() ( kutsuu += 1; return f(...argumentit); ) df.getCalls = function() ( paluukutsut; ) return df; )

Tämä tekee työskentelystä miellyttävämpää:


const csum = count((x, y) => x + y); csum(3, 7); // 10 csum(9, 6); // 15 csum.getCalls(); // 2

C neljäs kaikki on selvää: vaihdamme yksinkertaisesti FunctionExpression päällä ArrowFunction. Kiharan housunkannattimen puuttuminen varmistaa sen lyhyt huomautus nuolitoiminto jos hänen kehossaan on yksi ilme:


funktio count(f) ( anna kutsujen = 0; funktio df() ( kutsuu += 1; return f(...argumentit); ) df.getCalls = () => kutsut; paluu df; )

KANSSA kolmas- kaikki on monimutkaisempaa. Muistamme, että ensimmäinen asia, jonka teimme, oli vaihto FunctionExpression toimintoja vedota päällä FunctionDeclaration df. Jos haluat kirjoittaa tämän uudelleen ArrowFunction sinun on ratkaistava kaksi ongelmaa: et menetä pääsyä argumentteihin (tällä hetkellä tämä on pseudo-taulukko argumentteja) ja vältä kahden lausekkeen funktiorunkoa.


Funktiolle nimenomaisesti määritetty parametri auttaa meitä käsittelemään ensimmäistä ongelmaa args kanssa leviämisen operaattori. Ja yhdistääksesi kaksi lauseketta yhdeksi, voit käyttää looginen JA. Toisin kuin klassikko looginen operaattori konjunktiosta, joka palauttaa Boolen, se arvioi operandit vasemmalta oikealle ensimmäiseen "epätosi" ja palauttaa sen, ja jos kaikki ovat "tosia", niin viimeisen arvon. Aivan ensimmäinen laskurin lisäys antaa meille 1, mikä tarkoittaa, että tämä osalauseke pienenee aina tosi. Emme ole kiinnostuneita pelkistämään funktiokutsun tulosta toisessa osalausekkeessa "totuuteen": arvioija pysähtyy siihen joka tapauksessa. Nyt voimme käyttää ArrowFunction:


funktio count(f) ( olkoon kutsut = 0; olkoon df = (...args) => (kutsut += 1) && f(...args); df.getCalls = () => kutsut; return df; )

Voit koristella merkintää hieman käyttämällä etuliitteen lisäystä:


funktio count(f) ( anna kutsujen = 0; olkoon df = (...args) => ++kutsut && f(...args); df.getCalls = () => kutsut; return df; )

Ratkaisu ensimmäinen ja aloitetaan vaikeimmasta kohdasta vaihtamisesta Toimintoilmoitus päällä ArrowFunction. Mutta toistaiseksi meillä on edelleen vartalo kiharoissa:


const count = f => ( anna kutsujen = 0; anna df = (...args) => ++kutsut && f(...args); df.getCalls = () => puhelut; return df; );

Jos haluamme päästä eroon funktion runkoa ympäröivistä kihareista aaltosulkeista, meidän on vältettävä muuttujien ilmoittamista ja alustamista antaa. Ja meillä on kaksi muuttujaa: puhelut Ja df.


Ensin käsitellään laskuria. Voimme luoda paikallisen muuttujan määrittämällä sen funktioparametriluettelossa ja välittää alkuarvon kutsumalla IIFE (Immediately Invoked Function Expression):


const count = f => (kutsut => ( anna df = (...args) => ++kutsut && f(...args); df.getCalls = () => puhelut; return df; ))( 0);

Jäljelle jää vain ketjuttaa kolme lauseketta yhdeksi. Koska kaikki kolme lauseketta ovat funktioita, joiden arvo on aina tosi, voimme myös käyttää looginen JA:


const count = f => (puhelut => (df = (...args) => ++puhelut && f(...args)) && (df.getCalls = () => puhelut) && df)(0 );

Mutta on toinenkin vaihtoehto lausekkeiden ketjuttamiseen: using pilkkuoperaattori. Se on parempi, koska se ei sisällä tarpeettomia loogisia muunnoksia ja vaatii vähemmän sulkeita. Operandit arvioidaan vasemmalta oikealle, ja tuloksena on jälkimmäisen arvo:


const count = f => (kutsut => (df = (...args) => ++kutsut && f(...args), df.getCalls = () => puhelut, df))(0);

Ehkä onnistuin huijaamaan sinut? Pääsimme turvallisesti eroon muuttujan ilmoituksesta df ja jätti vain tehtävän nuolifunktiollemme. Tässä tapauksessa tämä muuttuja ilmoitetaan globaalisti, mikä ei ole hyväksyttävää! Toistetaan df paikallisen muuttujan alustus IIFE-funktiomme parametreissa, mutta emme ohita yhtään alkuarvo:


const count = f => ((puhelut, df) => (df = (...args) => ++puhelut && f(...args), df.getCalls = () => puhelut, df)) (0);

Tavoite on siis saavutettu.

Muunnelmia aiheesta

Mielenkiintoista on, että pystyimme välttämään paikallisten muuttujien luomisen ja alustamisen, useiden lausekkeiden funktiolohkoissa ja objektiliteraalin luomisen. Samalla he pitivät sen puhtaana alkuperäinen ratkaisu: globaalien muuttujien puuttuminen, laskurin yksityisyys, pääsy käärityn funktion argumentteihin.


Yleensä voit ottaa minkä tahansa toteutuksen ja yrittää tehdä jotain vastaavaa. Esimerkiksi polyfill funktiolle sitoa tässä suhteessa se on melko yksinkertainen:


const bind = (f, ctx, ...a) => (...args) => f.apply(ctx, a.concat(args));

Kuitenkin, jos väite f ei ole funktio, hyvällä tavalla meidän pitäisi tehdä poikkeus. Ja poikkeus heittää ei voida heittää pois ilmaisun yhteydessä. Voit odottaa heittolausekkeita (vaihe 2) ja yrittää uudelleen. Vai onko kenelläkään jo ajatuksia?


Tai harkitse luokkaa, joka kuvaa tietyn pisteen koordinaatit:


class Point ( konstruktori(x, y) ( this.x = x; this.y = y; ) toString() ( return `($(this.x), $(this.y))`; ) )

Joka voidaan esittää funktiolla:


const point = (x, y) => (p => (p.x = x, p.y = y, p.toString = () => ["(", x, ", ", y, ")"]. join (""), p))(uusi objekti);

Vain tässä olemme menettäneet prototyyppisen perinnön: toString on prototyyppiobjektin ominaisuus Kohta erikseen luodun objektin sijaan. Onko mahdollista välttää tämä, jos yrität kovasti?


Muutosten tulokset johtavat epäterveelliseen seokseen toiminnallinen ohjelmointi pakottavia hakkereita ja joitain itse kielen ominaisuuksia. Jos ajattelet sitä, tämä voisi olla mielenkiintoinen (mutta ei käytännöllinen) hämärälaite lähdekoodi. Voit keksiä oman versionsi ”haarukka-obfuskaattori”-ongelmasta ja viihdyttää JavaScripterin työtovereita ja ystäviä vapaa-ajallasi. hyödyllistä työtä aika.

Johtopäätös

Kysymys kuuluu, kenelle tämä on hyödyllistä ja miksi sitä tarvitaan? Tämä on täysin haitallista aloittelijoille, koska se luo väärän kuvan kielen liiallisesta monimutkaisuudesta ja poikkeavuudesta. Mutta siitä voi olla hyötyä harjoittajille, koska sen avulla voit tarkastella kielen ominaisuuksia toiselta puolelta: kehotus ei välttää, vaan kehotus yrittää välttää tulevaisuudessa.

Tunnisteet: Lisää tunnisteita

JavaScript on kaunis yksinkertaisella kielellä niille, jotka haluavat aloittaa sen oppimisen, mutta saavuttaakseen sen mestaruuden, sinun on ponnisteltava paljon.

Aloittelijat tekevät usein muutamia yleisiä virheitä, jotka kummittelevat heitä, kun he vähiten odottavat sitä. Jos haluat ymmärtää, mitä nämä virheet ovat, lue tämä artikkeli.

1. Kiharat olkaimet pois

Yksi JavaScript-aloittelijoiden usein tekemästä virheistä on kiharoiden aaltosulkeiden ohittaminen lausekkeiden, kuten if, else, while ja for , jälkeen. Vaikka tämä ei ole kiellettyä, sinun on oltava erittäin varovainen, koska se voi aiheuttaa piilotettu ongelma ja johtaa myöhemmin virheeseen.

Katso alla oleva esimerkki:

JS

// Tämä koodi ei tee sitä mitä sen pitäisi tehdä! if(nimi === määrittelemätön) console.log("Anna käyttäjänimi!"); epäonnistua(); // Suoritus ei koskaan saavuta tätä riviä: menestys(nimi); ) function success(name)( console.log("Hei, " + nimi + "!"); ) function fail())( throw new Error("Nimi puuttuu. Ei voi sanoa hei!"); )

Vaikka fail()-kutsu on sisennetty ja näyttää kuuluvan if-käskyyn, se ei kuulu. Sitä kutsutaan aina. Joten on hyvä käytäntö ympäröidä kaikki koodilohkot kiharasulkeilla, vaikka ne sisältävät vain yhden lauseen.

2. Ei puolipisteitä

JavaScript-jäsentämisen aikana prosessi tunnetaan nimellä automaattinen sijoitus puolipisteitä. Kuten nimestä voi päätellä, analysaattori täyttää puuttuvat merkit puolestasi.

Tämän ominaisuuden tarkoituksena on tehdä JavaScriptistä helpommin saavutettavissa ja helpompi kirjoittaa aloittelijoille. Puolipiste tulee kuitenkin aina lisätä itse, koska on olemassa vaara, että se puuttuu.

Tässä on esimerkki:

JS

// Tämän koodin käsittelyn tulos on virheilmoitus. Puolipisteen lisääminen ratkaisisi ongelman. console.log("Tervetuloa yhteisöön!") ["Frodo", "Gandalf", "Legolas", "Gimli"].forEach(funktio(nimi)( hei(nimi) )) function hello(nimi)( konsoli. log("Hei, " + nimi + "!") )

Koska rivillä 3 ei ole puolipistettä, jäsentäjä olettaa, että rivin 5 avaussulku on yritys käyttää ominaisuutta käyttämällä taulukon aksessorisyntaksia (katso bugi #8) erillisen taulukon sijaan, mikä ei ole sitä mitä sen oletettiin olla.

Tämä johtaa virheeseen. Korjaus on yksinkertainen - lisää aina puolipiste.

Jotkut kokeneet JavaScript-kehittäjät eivät halua käyttää puolipisteitä, mutta he tietävät sen hyvin mahdollisia virheitä ja miten niitä voidaan ehkäistä.

3. Väärinkäsitys tyyppivaloista

JavaScript tuettu dynaamiset tyypit. Tämä tarkoittaa, että sinun ei tarvitse määrittää tyyppiä uutta muuttujaa määritettäessä, vaan sen arvon voi vapaasti määrittää uudelleen tai muuntaa.

Tämä tekee JavaScriptistä paljon yksinkertaisempaa kuin esimerkiksi C# tai Java. Mutta tämä on täynnä mahdollista virheiden vaaraa, jotka muilla kielillä havaitaan käännösvaiheessa.

Tässä on esimerkki:

JS

// Odota syöttötapahtumaa tekstilaatikosta var textBox = document.querySelector("input"); textBox.addEventListener("input", function())( // textBox.value sisältää merkkijonon. 10:n lisääminen sisältää // merkkijonon "10", mutta ei lisää sitä. console.log(textBox.value + " + 10 = " + (textBox.value + 10));

HTML

Ongelma voidaan helposti korjata käyttämällä parseInt(textBox.value, 10) -komentoa muuntaaksesi merkkijonon numeroksi ennen kuin lisäät siihen luvun 10.

Riippuen siitä, kuinka käytät muuttujaa, ajonaika voi päättää, että se tulee muuntaa tyypiltään. Tätä kutsutaan tyyppivaluksi.

Voit estää tyypin muuntamisen verrattaessa muuttujia if-lauseessa tiukka tasa-arvotarkastus (=== ).

4. Unohtunut var

Toinen virhe, jonka aloittelijat tekevät, on se, että he unohtavat käyttää avainsana var muuttujia ilmoittaessaan. JavaScript on erittäin vapaa moottori.

Kun se näkee ensimmäistä kertaa, että olet käyttänyt muuttujaa ilman var-lausetta, se ilmoittaa sen automaattisesti globaalisti. Tämä voi johtaa joihinkin virheisiin.

Tässä on esimerkki, joka havainnollistaa myös toista virhettä - puuttuvaa pilkkua ilmoitettaessa useita muuttujia kerralla:

JS

var a = 1, b = 2, c = 3; funktio aakkoset(str)( var a = "A", b = "B" // Hups, puuttuu ","! c = "C", d = "D"; return str + " " + a + b + c + "..."; ) console.log(aakkoset("Sanotaan aakkoset!")); // Voi ei! Jotain on vialla! c:llä on uusi arvo! console.log(a, b, c );

Kun jäsentäjä saavuttaa rivin 4, se lisää automaattisesti puolipisteen ja tulkitsee sitten rivin 5 c- ja d-ilmoitukset globaaleiksi.

Tämä muuttaa toisen muuttujan c arvon. Lisää sudenkuoppia JavaScript täällä.

5. Liukulukuaritmetiikka

Tämä virhe on tyypillinen lähes kaikille ohjelmointikielille, mukaan lukien JavaScript. Sen tavan takia liukulukuluvut esitetään muistissa, aritmeettiset operaatiot ei toimi aivan niin kuin luulet.

Esimerkiksi:

JS

var a = 0,1, b = 0,2; // Yllätys! Tämä on väärin: console.log(a + b == 0,3); // Koska 0.1 + 0.2 ei vastaa odotuksiasi: console.log("0.1 + 0.2 = ", a + b);

Tämän ongelman kiertämiseksi sinun ei pitäisi käyttää desimaalilukuja, jos tarvitset ehdotonta tarkkuutta, käytä kokonaislukuja tai jos haluat työskennellä rahayksiköiden kanssa, käytä kirjastoa, kuten bignumber.js.

6. Konstruktoreiden käyttäminen alkuperäisen merkintätavan sijaan

Kun Java-ohjelmoijat ja C# alkavat kirjoittaa JavaScriptiä, he usein haluavat luoda objekteja rakentajien avulla: uusi Array(), uusi objekti(), uusi merkkijono().

JS

var elem4 = new Array(1,2,3,4); console.log("Neljän elementin taulukko: " + elem4.length); // Luo taulukko yhdestä elementistä. Tämä ei toimi niin kuin luulet: var elem1 = new Array(23); console.log("Yksi elementtitaulukko? " + elem1.length); /* Merkkijonoobjekteilla on myös omat ominaisuutensa */ var str1 = new String("JavaScript"), str2 = "JavaScript"; // Tiukkaa tasa-arvoa ei noudateta: console.log("Onko str1 sama kuin str2?", str1 === str2);

Ratkaisu tähän ongelmaan on yksinkertainen: yritä aina käyttää kirjaimellisia alkuperäisiä
nimitykset. Myöskään JS:ssä taulukoiden kokoa ei tarvitse määrittää etukäteen.

7. Väärinkäsitys siitä, miten alueet jaetaan

Yksi JS-aloittelijoiden vaikeimmista asioista on alueiden rajaamisen ja sulkemisen säännöt. Ja se ei todellakaan ole helppoa:

JS

for(var i = 0; i< 10; i++){ setTimeout(function(){ console.log(i+1); }, 100*i); } /* Чтобы исправить проблему, заключите код в выражение самовыполняющейся функции: for(var i = 0; i < 10; i++){ (function(i){ setTimeout(function(){ console.log(i+1); }, 100*i); })(i); } */

Funktiot pysyvät liitettyinä niiden ylätason muuttujiin. Mutta koska viivytämme suoritusta setTimeout : n kautta, kun funktiot tulee suorittaa, silmukka itse asiassa päättyy ja i-muuttuja kasvaa arvoon 11.

Itsesuorituskykyinen funktio kommenteissa toimii, koska se kopioi muuttujan i arvon ja tallentaa siitä kopion joka kerta, kun toiminto suoritetaan. Voit lukea lisää sarjoista täältä ja täältä.

8. Eval

Eval on paha. Tätä pidetään huonona käytäntönä, ja useimmissa tapauksissa, kun ajattelet sen tekevän, on olemassa parempi ja nopeampi tapa.

JS

// Tämä on huono käytäntö. Älä tee näin: console.log(eval("obj.nimi + " on " + obj." + access)); // Käytä sen sijaan merkintätaulukkoa saadaksesi ominaisuuksia dynaamisesti: console.log(obj.name + " on " + obj); /* Evalin käyttö setTimoutissa */ // Tämä on myös huono käytäntö. Se on hidasta ja vaikeaa testata ja korjata: setTimeout(" if(obj.age == 30) console.log("Tämä on eval-ed-koodi, " + obj + "!");", 100); // Se on parempi näin: setTimeout(function())( if(obj.age == 30)( console.log("Tätä koodia ei eval-ed, " + obj + "!"); ) ) , 100);

Evalin sisällä oleva koodi on merkkijono. Eval-lohkoihin liittyvät virheenkorjausviestit ovat hämmentäviä, ja sinun on ryhdyttävä aivoihin saadaksesi kerta- ja kaksoislainausmerkit oikein.

Puhumattakaan siitä, että se on hitaampi kuin tavallinen JavaScript. Älä käytä Evalia, ellet tiedä tarkalleen mitä olet tekemässä.

9. Asynkronisen koodin väärinkäsitys

Se, mikä todella tekee JavaScriptistä erottuvan ja ainutlaatuisen, on se, että melkein kaikki toimii asynkronisesti ja sinun on läpäistävä toimintoja soita takaisin saadakseen ilmoituksia tapahtumista.

Aloittelijoiden on edelleen vaikea ymmärtää tätä intuitiivisesti, ja he joutuvat nopeasti umpikujaan, kun he kohtaavat vaikeasti ymmärrettäviä virheitä.

Tässä on esimerkki, jossa käytän FreeGeoIP-palvelua sijaintisi määrittämiseen IP-osoitteen perusteella:

JS

// Määritä nykyisen käyttäjän sijaintitiedot. ladata(); // Näytä käyttäjän sijainti. Oho, se ei toimi! Miksi? console.log("Hei! IP-osoitteesi on " + userData.ip + " ja maasi on " + userData.country_name); // Ladattu funktio määrittää nykyisen käyttäjän ip:n ja sen sijainnin // ajaxin kautta freegeoip-palvelun avulla. Kun tämä on tehty, se sijoittaa palautetut //-tiedot userData-muuttujaan. function load())( $.getJSON("http://freegeoip.net/json/?callback=?", function(response)( userData = vastaus; // Tulosta seuraava rivi kommenteista nähdäksesi // palautettu tulos: // console.log(response));

Vaikka console.log sijaitsee load()-funktiokutsun jälkeen, se suoritetaan itse asiassa ennen tietojen määrittelyä.

10. Tapahtumaseurannan väärinkäyttö

Oletetaan, että haluat seurata painikkeen napsautusta, mutta vain, jos tarkistus on asennettu.

Ohjelman virheitä on melko vaikea löytää. Tehtäväsi helpottamiseksi sinun on kirjoitettava koodi oikein ja kauniisti (luettavissa). Hyvä koodi on oltava luettavissa, johdonmukainen, ennakoitavissa, oikein muotoiltu ja siinä on oltava myös dokumentaatio.

Tärkeitä ohjeita koodin muotoilusta

Sisennykset

Jos koodia ei sisennetä, sitä ei voi lukea. Tyypillisesti sisennykset tehdään sarkainmerkillä, mutta on myös sellaisia, jotka sisentävät välilyöntejä, yleensä 3-4 välilyöntiä. Alla on esimerkki oikeasta ja väärästä sisennyksestä:

/* Oikea sisennys */

< max; i++) {
jos (arr[i] % 2 === 0) (
vastaus =(
summa: summa += arr[i]
};
}
}
/* Ja täällä kaikki on huonosti */
var vastaus, arr = , summa = 0;
for (var i = 0, max = arr.length; i< max; i++) {
jos (arr[i] % 2 === 0) (
vastaus =(
summa: summa += arr[i]
};
}
}

Aaltosulkeet

Käytä aina kiharoita, vaikka niitä ei tarvittaisikaan. Teknisestä näkökulmasta katsottuna, jos if-, while- tai for-konstruktin runko koostuu yhdestä lauseesta, kiharat aaltosulut voidaan jättää pois. Kun kiharat aaltosulkeet on aina kirjoitettu, on helpompi tehdä muutoksia myöhemmin. Alla on esimerkki siitä, kuinka se tehdään oikein ja mitä ei:

/* Oikea kirjoitus */
for (var i = 0; i< 5; i++) {
hälytys(i);
}
/* Mutta ei ole yhtä */
for (var i = 0; i< 5; i++)
hälytys(i);

Avaustuen sijainti

Ohjelmoijia on kahdenlaisia:

Jos (a > b) (
...
}
jos (a > b)
{
...
}

Aloituskiharan aaltosulkeen kirjoittaminen on sinun päätettävissäsi. Tiedämme jo, että JavaScript itse lisää puolipisteitä ja tässä ongelma ilmenee toisessa menetelmässä. Oletetaan, että kirjoitat tämän koodinpätkän:

Palata
{
id: 1
};

Ja mitä tulkki tekee? Hän lisää puolipisteen returnin jälkeen ja käy ilmi, että funktio palauttaa määrittelemättömän. Eli tulkki näkee tämän koodinpätkän näin:

Palautus määrittelemätön;
{
id: 1
};

Spaces

Välilyöntejä tulee käyttää oikein. Kun kirjoitat paperille, jätätkö pilkun jälkeen tyhjää tilaa? Taitaa olla kyllä. Ja kun luet jotain JavaScriptissä, sinun on laitettava välilyönti pilkun jälkeen. Mihin minun pitäisi laittaa välilyönti, jotta koodi olisi selkeä?

1. Puolipisteiden jälkeen for-lauseessa

For (var i = 0; i< 100; i++) {...}

2. Kun julistetaan useita muuttujia for-lauseessa

For (var i = 0, max = arr.length; i< max; i++) {...}

3. Pilkkujen jälkeen taulukon elementtien välissä

JavaScript-koodin oppitunnin kolmas osa. Yksinkertaiset suositukset luoda helposti ylläpidettävä projekti.

Vältä oletettuja tyyppimuunnoksia

JavaScript sisältää muuttujatyyppien muuntamisen, kun niitä verrataan. Siksi vertailut, kuten false == 0 tai "" == 0, palauttavat true .

Välttääksesi implisiittisen tyyppimuunnoksen aiheuttamia ongelmia, käytä aina === ja !==-operaattoreita tarkistaaksesi sekä verrattavien lausekkeiden arvot että tyypit:

On toinenkin ohjelmointikoulu, joka pitää ===-operaattorin liikakäyttöä, kun ==-operaattori riittää. Esimerkiksi käytettäessä typeof-funktiota, joka palauttaa merkkijonon, ei ole mitään syytä vaatia tiukkaa vastaavuutta. Mutta JSLint vaatii tiukkaa noudattamista. Ja lisäksi koodi näyttää johdonmukaisemmalta ja vähentää ajattelun määrää luettaessa (" Tämä operaattori== käytetty tarkoituksella vai vahingossa?").

Vältä eval()

Funktio eval() ottaa mielivaltaisen merkkijonon ja suorittaa sen JavaScript-koodina. Jos koodi on tiedossa (eikä määritetä prosessin suorittamisen aikana), ei ole mitään syytä käyttää eval():ta ollenkaan. Jos koodi generoidaan dynaamisesti ajon aikana, tavoite on usein mahdollista saavuttaa paras tapa kuin käyttämällä eval() hakasulkeet dynaaminen pääsy kiinteistöihin on parempi ja yksinkertaisempi:

// huono var property = "nimi"; alert(eval("obj." + ominaisuus)); // parempi tehdä tämä var property = "nimi"; hälytys(obj);

Eval():n käyttämisellä voi olla myös turvallisuusvaikutuksia, koska saatat suorittaa haitallista koodia (esimerkiksi verkosta saatua). Melko yleinen huono käytäntö työskenneltäessä JSON-vastauksen kanssa AJAX-pyyntö. SISÄÄN tässä tapauksessa On parempi käyttää selaimen sisäänrakennettuja menetelmiä JSON-vastauksen jäsentämiseen ongelman ratkaisemiseksi turvallinen menetelmä ja aivan oikein. Selaimet, jotka eivät tue JSON.parse(), voit käyttää JSON.org:n kirjastoa.

On myös tärkeää muistaa, että merkkijonojen välittäminen setInterval() , setTimeout() ja Function()-konstruktorille on samanlaista kuin eval():n käyttäminen useimmissa tapauksissa. Siksi tällaisia ​​toimia tulisi välttää. Taustalla JavaScript arvioi ja suorittaa syöttämäsi merkkijonot ohjelmakoodi:

// huono setTimeout("myFunc()", 1000); setTimeout("myFunc(1, 2, 3)", 1000); //mieluiten setTimeout(myFunc, 1000); setTimeout(funktio () ( myFunc(1, 2, 3); ), 1000);

Uuden Function()-konstruktorin käyttö on samanlaista kuin eval() ja sitä tulee käyttää varoen. Tämä voimakas työkalu, mutta sitä käytetään usein väärin. Jos sinun on ehdottomasti käytettävä eval() -ohjelmaa, harkitse sen käyttöä uuden käyttö Funktio() . Pieni mahdollinen hyöty on siinä, että new Function():ssa määritetty koodi toimii funktion paikallisessa avaruudessa, joten määritetyn koodin var-direktiivillä määritetyt muuttujat eivät automaattisesti muutu globaaliksi. Toinen tapa välttää automaattinen tunnistus globaalit muuttujat - kääri eval()-kutsu funktioon.

Harkitse seuraavaa esimerkkiä. Tässä vain un jää globaaliksi muuttujaksi, joka saastuttaa nimiavaruuden:

Console.log(typeof un); // "määrittelemätön" console.log(typeof deux); // "määrittelemätön" console.log(typeof trois); // "määrittelemätön" var jsstring = "var un = 1; console.log(un);"; eval(jsstring); // Lokit "1" jsstring = "var deux = 2; console.log(deux);"; uusi Function(jsstring)(); // Lokit "2" jsstring = "var trois = 3; console.log(trois);"; (funktio () ( eval(jsstring); )()); // Loki "3" console.log(typeof un); // numerokonsoli.log(typeof deux); // määrittelemätön konsoli.log(typeof trois); // määrittelemätön

Toinen ero eval():n ja uuden Function()-konstruktorin välillä on se, että eval() voi mennä päällekkäin nimiavaruusketjun kanssa, kun taas funktion suoritus tapahtuu hiekkalaatikossa. Sillä ei ole väliä missä suoritat Function , se käyttää vain yleistä nimiavaruutta. Siksi se saastuttaa paikallista nimiavaruutta vähemmän. Seuraavassa esimerkissä eval() voi käyttää ja muokata muuttujia ulkoisessa nimiavaruudessaan, mutta Function ei voi (funktion ja new Functionin käyttö on identtinen):

(funktio () ( var local = 1; eval("local = 3; console.log(local)"); // Loki "3" console.log(local); // Loki "3" )()); (funktio () ( var local = 1; Function("console.log(typeof local);")(); // Loki "undefined" )());

Luvun muuntaminen käyttämällä parseInt()

Käyttämällä parseInt() voit saada numeron merkkijonosta. Funktio ottaa toisen parametrin - numerojärjestelmän perustan, joka usein jätetään pois. Mutta turhaan. Ongelma ilmenee, kun sinun on jäsennettävä nollalla alkava merkkijono: esimerkiksi osa päivämäärästä, joka syötetään lomakekenttään. 0:lla alkavaa merkkijonoa käsitellään nimellä oktaaliluku(kantanimi 8), joka määritettiin ECMAScript 3:ssa (mutta muutettu ECMAScript 5:ssä). Välttääksesi yhteensopimattomuudet ja odottamattomat tulokset, sinun tulee aina käyttää radix-parametria:

Muuttuva kuukausi = "06", vuosi = "09"; kuukausi = parseInt(kuukausi, 10); vuosi = parseInt(vuosi, 10);

Jos tässä esimerkissä jätät pois kantalukuparametrin (kutsu funktiota nimellä parseInt(year)), saat arvon 0, koska "09" oletetaan olevan oktaaliluku (ikään kuin olisit kutsunut parseInt(year, 8)) ), ja 09 on virheellinen luku kantaluvussa 8.

Vaihtoehtoiset menetelmät merkkijonon muuntaminen luvuksi:

+"08" // tulos 8 Numero("08") // 8

Nämä menetelmät ovat usein nopeampia kuin parseInt(), koska parseInt() jäsentää merkkijonoja yksinkertaisen muuntamisen sijaan. Mutta jos oletetaan, että syöte saattaa olla muodossa "08 hello", parseInt() palauttaa luvun ja muut menetelmät epäonnistuvat ja palauttavat NaN .

Koodivaatimukset

On tärkeää laatia koodivaatimukset ja noudattaa niitä - tällainen vaihe tekee koodista johdonmukaisen, ennustettavan ja huomattavasti helpomman ja ymmärrettävän. Uusi kehittäjä koodivaatimuksia noudattaen tiimisi pääsee työrytmiin paljon nopeammin ja hyväksyy muiden projektiin osallistujien kirjoittaman koodin.

Vakavia tappeluita ja välienselvittelyjä syntyy, kun keskustellaan koodivaatimusten eri näkökohdista (esimerkiksi siitä, sisennetäänkö ne välilyönneillä tai sarkaimilla). Siksi, kun otat käyttöön koodivaatimuksia, sinun on valmistauduttava vakavasti keskusteluihin. Mutta koodivaatimukset ja niiden tiukka noudattaminen ovat erittäin tärkeitä projektin olemassaolon ja onnistuneen kehityksen kannalta.

Sisennykset

Jotkut kehittäjät käyttävät mieluummin sarkaimia sisennyksiin, koska jokainen voi määrittää editorinsa tulostamaan tietyn määrän välilyöntejä sarkaimien sijaan omien mieltymystensä mukaan. Toiset käyttävät mieluummin välilyöntejä. Asian pohjimmiltaan tämä ei ole tärkeää, tärkeintä on, että sisennykset määritellään nykyisissä koodivaatimuksissa.

Mihin syvennykset pitäisi tehdä? Sääntö on yksinkertainen - kaikkialla, missä on kiharat henkselit. Toisin sanoen funktioiden rungossa silmukat (do, while, for, for-in), if- ja switch-lauseet ja objektin ominaisuudet. Seuraava koodi näyttää esimerkkejä sisennyksen käytöstä:

Funktio ulompi(a, b) ( var c = 1, d = 2, sisempi; if (a > b) ( sisempi = funktio () ( paluu ( r: c - d ); ) ) else ( sisempi = funktio ( ) ( paluu ( r: c + d ); ) paluu sisäinen;

Aaltosulkeet

Kiharaisia ​​henkselit tulee aina käyttää, myös silloin, kun ne ovat vaihtoehtoja. Teknisesti, kun sinulla on vain yksi lauseke if- tai for-lauseessa, kiharat aaltosulkeet eivät ole pakollisia, mutta niitä tulee kuitenkin käyttää. Ne tekevät koodista johdonmukaisemman ja helpommin laajennettavan.

Kuvittele, että sinulla on silmukka yhdellä lausekkeella. Voit jättää sulkeet pois, mikä ei tapahdu syntaksivirhe:

// huono for (var i = 0; i< 10; i += 1) alert(i);

Mutta entä jos joudut myöhemmin lisäämään toisen rivin silmukan runkoon?

// huono for (var i = 0; i< 10; i += 1) alert(i); alert(i + " is " + (i % 2 ? "odd" : "even"));

Toinen hälytystoiminto on silmukan ulkopuolella, ja sisennys voi olla sinulle huono vitsi. Tulevaisuudessa on parasta käyttää aina sulkeita, jopa yksirivisissä lohkoissa:

// mieluiten for (var i = 0; i< 10; i += 1) { alert(i); }

Myös ehdot:

// bad if (true) alert(1); else alert(2); // mieluiten jos (tosi) (hälytys(1); ) else ( alert(2); )

Avaa kiinnikkeen asento

Usein herää kysymys: mihin avoimet sulut tulisi sijoittaa - samalle riville vai seuraavalle?

Jos (tosi) (hälytys("Viesti!");)

Jos (tosi) (hälytys ("Viesti"); )

Tässä esimerkissä kiinnikkeen sijainti on mieltymyskysymys. Mutta on tapauksia, joissa ohjelma käyttäytyy eri tavalla kiinnikkeen sijainnin mukaan. Tämä tilanne johtuu puolipisteen lisäysmekanismista - JavaScript ei ymmärrä, kun päätät olla lopettamatta riviä oikein, ja lisää puolipisteen puolestasi. Tämä toiminta voi aiheuttaa ongelmia, kun funktio palauttaa kirjaimellisen objektin ja avoimet sulut ovat kohdassa seuraava rivi:

// varoitus: odottamaton paluufunktio func() ( return // seuraa koodilohkoa, jota ei koskaan suoriteta (nimi: "Batman") )

Jos sitä odotit tämä toiminto palauttaa esineen, jolla on nimiominaisuus, yllätyt ikävästi. Oletetun puolipisteen vuoksi funktio palauttaa undefined . Edellinen koodi on vastaava seuraava lohko:

// varoitus: odottamaton paluufunktio func() ( return undefined; // seuraa koodilohkoa, jota ei koskaan suoriteta (nimi: "Batman") )

Funktio func() ( return ( nimi: "Batman"); )

Huomaa puolipiste. Kuten kiharat aaltosulkeet, sinun tulee aina käyttää puolipistettä, vaikka konstruktio viittaa siihen JavaScript-koodi. Tämä ei ainoastaan ​​luo kurinalaisuutta ja vastaa tiukempaa lähestymistapaa koodaukseen, vaan se auttaa myös välttämään yllä olevan esimerkin kaltaisia ​​epäselviä tilanteita.