JavaScript-toiminnot. Expressive JavaScript: Funktiot Toimivat arvoina

Ihmiset ajattelevat, että tietojenkäsittely on taidetta neroille. Todellisuudessa asia on päinvastoin - vain monet ihmiset tekevät asioita, jotka seisovat päällekkäin, ikään kuin muodostaisivat seinän pienistä kivistä.

Donald Knuth

Olet jo nähnyt toimintokutsuja, kuten hälytys. Funktiot ovat JavaScript-ohjelmoinnin leipä ja voita. Ajatus ohjelman osan käärimisestä ja sen kutsumisesta muuttujaksi on erittäin suosittu. Se on työkalu suurten ohjelmien jäsentämiseen, toiston vähentämiseen, nimien antoon aliohjelmille ja aliohjelmien eristämiseen toisistaan.

Ilmeisin funktioiden käyttötapa on uuden sanakirjan luominen. Sanojen keksiminen tavalliselle ihmisproosalle on huono muoto. Ohjelmointikielessä tämä on välttämätöntä.

Keskimääräinen aikuinen venäjänkielinen osaa noin 10 000 sanaa. Harvinainen ohjelmointikieli sisältää 10 000 sisäänrakennettua komentoa. Ja ohjelmointikielen sanasto on selkeämmin määritelty, joten se on vähemmän joustava kuin ihmisen. Siksi meidän on yleensä lisättävä siihen omat sanamme tarpeettoman toiston välttämiseksi.

Toiminnon määritelmä

Funktiomäärittely on normaali muuttujan määritelmä, jossa muuttujan vastaanottama arvo on funktio. Esimerkiksi seuraava koodi määrittelee muuttujan neliön, joka viittaa funktioon, joka laskee tietyn luvun neliön:

var square = funktio(x) ( paluu x * x; ); console.log(neliö(12)); // → 144

Funktio luodaan lausekkeella, joka alkaa funktion avainsanalla. Funktioilla on joukko parametreja (tässä tapauksessa vain x) ja runko, joka sisältää käskyt, jotka suoritetaan funktiota kutsuttaessa. Funktion runko on aina suljettu aaltosulkeisiin, vaikka se koostuisi yhdestä lauseesta.

Funktiolla voi olla useita parametreja tai ei ollenkaan. Seuraavassa esimerkissä makeNoise ei sisällä parametriluetteloa, kun taas teholla on kaksi:

Muutt makeNoise = function() ( console.log("Vittu!"); ); makeNoise(); // → Paskaa! var teho = funktio(kanta, eksponentti) ( var tulos = 1; for (muut count = 0; count< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Jotkut funktiot palauttavat arvon, kuten power ja square, toiset eivät, kuten makeNoise, jolla on vain sivuvaikutus. Return-käsky määrittää funktion palauttaman arvon. Kun ohjelman käsittely saavuttaa tämän käskyn, se poistuu välittömästi funktiosta ja palauttaa tämän arvon siihen koodiin, josta funktiota kutsuttiin. return ilman lauseketta palauttaa määrittelemättömän .

Parametrit ja laajuus

Funktioparametrit ovat aivan kuin muuttujia, mutta niiden alkuarvot asetetaan funktiota kutsuttaessa, ei sen koodissa.

Tärkeä funktioiden ominaisuus on, että funktion sisällä luodut muuttujat (mukaan lukien parametrit) ovat paikallisia kyseisessä funktiossa. Tämä tarkoittaa, että tehoesimerkissä muuttujatulos luodaan joka kerta kun funktiota kutsutaan, eivätkä nämä sen erilliset inkarnaatiot liity toisiinsa millään tavalla.

Tämä muuttujien paikkakunta koskee vain funktioiden sisällä luotuja parametreja ja muuttujia. Minkä tahansa funktion ulkopuolelle asetettuja muuttujia kutsutaan globaaleiksi muuttujiksi, koska ne ovat näkyvissä koko ohjelmassa. Voit käyttää tällaisia ​​muuttujia myös funktion sisällä, ellet ole ilmoittanut samannimistä paikallista muuttujaa.

Seuraava koodi havainnollistaa tätä. Se määrittelee ja kutsuu kaksi funktiota, jotka antavat arvon x:lle. Ensimmäinen ilmoittaa sen paikalliseksi ja muuttaa siten vain paikallista muuttujaa. Toinen ei julista, joten työskentely x:n kanssa funktion sisällä viittaa globaaliin muuttujaan x, joka asetettiin esimerkin alussa.

var x = "ulkopuolella"; var f1 = funktio() ( var x = "f1:n sisällä"; ); f1(); konsolin loki(x); // → ulkopuolella var f2 = function() ( x = "f2:n sisällä"; ); f2(); konsolin loki(x); // → f2:n sisällä

Tämä toiminta auttaa estämään tahatonta vuorovaikutusta toimintojen välillä. Jos kaikkia muuttujia käytettäisiin missä tahansa ohjelmassa, olisi erittäin vaikeaa varmistaa, ettei yhtä muuttujaa käytetä eri tarkoituksiin. Ja jos käyttäisit muuttujaa uudelleen, törmäät outoihin vaikutuksiin, joissa kolmannen osapuolen koodi sekoittaa muuttujasi arvot. Käsittelemällä funktio-paikallisia muuttujia niin, että ne ovat olemassa vain funktion sisällä, kieli mahdollistaa funktioiden työskentelyn ikään kuin ne olisivat erillisiä pieniä universumeja, jolloin ei tarvitse huolehtia koko koodista kokonaisuutena.

Sisäkkäiset kaukoputket

JavaScript ei tee eroa vain globaalien ja paikallisten muuttujien välillä. Funktiot voidaan määritellä funktioiden sisällä, mikä johtaa useisiin paikallisuustasoihin.

Esimerkiksi seuraava melko merkityksetön funktio sisältää vielä kaksi sisällä:

var maisema = funktio() ( var tulos = ""; var flat = funktio(koko) ( for (muut count = 0; count< size; count++) result += "_"; }; var mountain = function(size) { result += "/"; for (var count = 0; count < size; count++) result += """; result += "\\"; }; flat(3); mountain(4); flat(6); mountain(1); flat(1); return result; }; console.log(landscape()); // → ___/""""\______/"\_

Tasa- ja vuoristofunktiot näkevät tulosmuuttujan, koska ne ovat sen funktion sisällä, jossa se on määritelty. Mutta he eivät voi nähdä toistensa lukumuuttujia, koska yhden funktion muuttujat ovat toisen funktion ulkopuolella. Ja maisemafunktion ulkopuolella oleva ympäristö ei näe mitään tämän funktion sisällä määritellyistä muuttujista.

Lyhyesti sanottuna jokaisessa paikallisessa laajuudessa näet kaikki laajuudet, jotka sisältävät sen. Funktiossa käytettävissä olevien muuttujien joukko määräytyy sen paikan mukaan, missä tämä funktio on ilmoitettu ohjelmassa. Kaikki muuttujat funktiomäärittelyä ympäröivistä lohkoista ovat näkyvissä - mukaan lukien ne, jotka on määritelty huipputaso pääohjelmassa. Tätä lähestymistapaa laajuuksiin kutsutaan leksikaaliseksi.

Ihmiset, jotka ovat opiskelleet muita ohjelmointikieliä, saattavat ajatella, että mikä tahansa aaltosulkeisiin suljettu lohko luo oman paikallisen ympäristönsä. Mutta JavaScriptissä vain funktiot luovat laajuuden. Voit käyttää itsenäisiä lohkoja:

var jotain = 1; ( var something = 2; // Tee jotain muuttujalla jotain... ) // Poistu lohkosta...

Mutta jokin lohkon sisällä on sama muuttuja kuin sen ulkopuolella. Vaikka tällaiset lohkot ovat sallittuja, on järkevää käyttää niitä vain if-lauseisiin ja silmukoihin.

Jos tämä tuntuu sinusta oudolta, se ei vaikuta siltä vain sinusta. JavaScript 1.7 esitteli avainsanan let, joka toimii kuten var, mutta luo muuttujat, jotka ovat paikallisia mille tahansa lohkolle, ei vain funktiolle.

Toimii arvoina

Toimintojen nimiä käytetään yleensä ohjelman osan nimissä. Tällainen muuttuja on kerran asetettu, eikä se muutu. Siksi funktio on helppo sekoittaa sen nimeen.

Mutta nämä ovat kaksi eri asiaa. Funktiokutsua voidaan käyttää kuin yksinkertaista muuttujaa - niitä voidaan esimerkiksi käyttää missä tahansa lausekkeessa. On mahdollista tallentaa funktiokutsu uuteen muuttujaan, siirtää se parametrina toiselle funktiolle ja niin edelleen. Myös funktiokutsun tallentava muuttuja pysyy tavallisena muuttujana ja sen arvoa voidaan muuttaa:

Var launchMissiles = function(arvo) ( ​​missileSystem. launch("ole hyvä!"); ); if (safeMode) launchMissiles = function(arvo) (/* release */);

Luvussa 5 käsittelemme upeita asioita, joita voidaan tehdä välittämällä funktiokutsuja muille funktioille.

Toiminnan ilmoitus

Lausekkeesta "var square = funktio..." on olemassa lyhyempi versio. Funktioavainsanaa voidaan käyttää lauseen alussa:

funktio neliö(x) (paluu x * x; )

Tämä on funktion ilmoitus. Lause määrittää muuttujan neliön ja määrittää sille annetun funktion. Toistaiseksi kaikki on ok. Tällaisessa määritelmässä on vain yksi sudenkuoppa.

Console.log("Tulevaisuus sanoo:", future()); function future() ( return "Meillä ei ole vielä lentäviä autoja."; )

Tämä koodi toimii, vaikka toiminto on ilmoitettu sitä käyttävän koodin alapuolella. Tämä johtuu siitä, että funktioilmoitukset eivät ole osa ohjelmien normaalia ylhäältä alas -suoritusta. Ne "siirtyvät" soveltamisalansa huipulle ja niitä voidaan kutsua millä tahansa kyseisessä laajuudessa olevalla koodilla. Tämä on joskus kätevää, koska voit kirjoittaa koodin järkevimmässä järjestyksessä ilman, että sinun tarvitsee huolehtia siitä, että sinun on määritettävä kaikki yllä olevat toiminnot, missä niitä käytetään.

Mutta mitä tapahtuu, jos asetamme funktion ilmoituksen ehdollisen lohkon tai silmukan sisään? Sinun ei tarvitse tehdä sitä. Historiallisesti eri alustat JavaScriptin suorittamiseksi käsittelivät tällaisia ​​tapauksia eri tavalla, ja nykyinen kielistandardi kieltää sen. Jos haluat ohjelmien toimivan johdonmukaisesti, käytä funktiomäärityksiä vain muiden funktioiden tai pääohjelman sisällä.

Funktio esimerkki() ( funktio a() () // Normule if (jotain) ( funktio b() () // Ay-yy-yy! ) )

kutsupino
On hyödyllistä tarkastella lähemmin, kuinka suoritusjärjestys toimii funktioiden kanssa. Tässä yksinkertainen ohjelma useilla funktiokutsuilla:

Funktio greet(who) ( console.log("Hei, " + kuka); ) greet("Semjon"); console.log("Pokeda");

Se käsitellään jotakuinkin näin: tervehdyksen kutsuminen saa passin hyppäämään funktion alkuun. Se kutsuu sisäänrakennettua console.log-funktiota, joka ottaa hallinnan, tekee tehtävänsä ja palauttaa ohjauksen. Sitten se saavuttaa tervehdyksen loppuun ja palaa paikkaan, josta se kutsuttiin. Seuraava rivi kutsuu uudelleen console.logia.

Kaavamaisesti tämä voidaan esittää seuraavasti:

Top greet console.log greet top console.log top

Koska funktion on palattava sinne, mistä se kutsuttiin, tietokoneen on muistettava konteksti, josta funktio kutsuttiin. Yhdessä tapauksessa console.login pitäisi muuttua takaisin tervehtimään. Toisessa se palaa ohjelman loppuun.

Paikkaa, jossa tietokone muistaa kontekstin, kutsutaan pinoksi. Joka kerta kun funktiota kutsutaan, nykyinen konteksti työnnetään pinon päälle. Kun funktio palaa, se ponnahtaa ylimmän kontekstin pinosta ja käyttää sitä jatkaakseen.

Pinon tallennus vaatii muistitilaa. Kun pino kasvaa liian suureksi, tietokone lopettaa suorittamisen ja antaa jotain, kuten "pinon ylivuoto" tai "liian paljon rekursiota". Seuraava koodi osoittaa tämän - se kysyy tietokoneelta erittäin monimutkaisen kysymyksen, joka johtaa loputtomiin hyppyihin kahden toiminnon välillä. Tarkemmin sanottuna se olisi ääretöntä hyppyä, jos tietokoneessa olisi ääretön pino. Todellisuudessa pino vuotaa yli.

Funktio kana() ( return egg(); ) function egg() ( return chicken(); ) console.log(kana() + " tuli ensin."); // → ??

Valinnaiset argumentit
Seuraava koodi on täysin laillinen ja toimii ilman ongelmia:

Alert("Hei", "Hyvää iltaa", "Hei kaikille!");

Virallisesti funktiolla on yksi argumentti. Näin haastateltuna hän ei kuitenkaan valittaa. Se jättää huomioimatta loput argumentit ja näyttää "Hei".

JavaScript on erittäin lempeä funktiolle välitettävien argumenttien lukumäärän suhteen. Jos ohitat liikaa, ylimääräiset ohitetaan. Liian vähän – puuttuville annetaan arvo määrittelemätön.

Tämän lähestymistavan haittana on, että on mahdollista - ja jopa todennäköistä - siirtää funktiolle väärä määrä argumentteja, eikä kukaan valita sinulle tästä.

Kääntöpuolena on, että voit luoda funktioita, jotka ottavat valinnaisia ​​argumentteja. Esimerkiksi tehofunktion seuraavassa versiossa sitä voidaan kutsua sekä kahdella että yhdellä argumentilla - jälkimmäisessä tapauksessa eksponentti on yhtä suuri kuin kaksi ja funktio toimii kuin neliö.

Funktioteho(kanta, eksponentti) ( if (eksponentti == määrittelemätön) eksponentti = 2; var tulos = 1; for (var count = 0; count< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

Seuraavassa luvussa nähdään, kuinka funktion runko voi kertoa sille välitettyjen argumenttien tarkan määrän. Tämä on hyödyllistä, koska voit luoda funktion, joka ottaa minkä tahansa määrän argumentteja. Esimerkiksi console.log käyttää tätä ominaisuutta ja tulostaa kaikki sille välitetyt argumentit:

Console.log("R", 2, "D", 2); // → R 2 D 2

Sulkemiset

Mahdollisuus käyttää funktiokutsuja muuttujina yhdistettynä siihen, että paikalliset muuttujat luodaan uudelleen joka kerta, kun funktiota kutsutaan, tuo meidät mielenkiintoiseen kohtaan. Mitä tapahtuu paikallisille muuttujille, kun toiminto epäonnistuu?

Seuraava esimerkki havainnollistaa tätä ongelmaa. Se ilmoittaa wrapValue-funktion, joka luo paikallisen muuttujan. Sitten se palauttaa funktion, joka lukee kyseisen paikallisen muuttujan ja palauttaa sen arvon.

Funktio wrapValue(n) ( var localVariable = n; return function() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapArvo(2); konsolin loki(wrap1()); // → 1 konsoli.log(wrap2()); // → 2

Tämä on pätevä ja toimii kuten pitääkin - pääsy muuttujaan säilyy. Lisäksi saman muuttujan useita esiintymiä voi esiintyä samanaikaisesti, mikä vahvistaa entisestään sitä tosiasiaa, että paikalliset muuttujat luodaan uudelleen jokaisen funktiokutsun yhteydessä.

Tätä kykyä toimia viittauksen kanssa johonkin paikallisen muuttujan esiintymään kutsutaan sulkemiseksi. Toimintoa, joka sulkee paikalliset muuttujat, kutsutaan sulkemisfunktioksi. Se ei ainoastaan ​​vapauta sinua murehtimasta muuttuvista käyttöioista, vaan sen avulla voit myös käyttää toimintoja luovasti.

Pienellä muutoksella teemme esimerkistämme funktion, joka kertoo numerot millä tahansa annetulla luvulla.

Funktiokerroin(kerroin) ( paluufunktio(luku) ( paluuluku * tekijä; ); ) var double = kerroin(2); console.log(twice(5)); // → 10

Erillistä muuttujaa, kuten localVariable wrapValue-esimerkistä, ei enää tarvita. Koska parametri itsessään on paikallinen muuttuja.

Vaatii harjoittelua, että alkaa ajatella näin. Hyvä versio mentaalimallista on kuvitella, että funktio jäädyttää koodin kehoonsa ja kääriä sen pakkaukseen. Kun näet paluufunktion(...) (...), ajattele sitä ohjauspaneelina jäädytetylle koodinpalalle myöhempää käyttöä varten.

Esimerkissämme kerroin palauttaa jäädytetyn koodinpätkän, jonka tallennamme kaksinkertaiseen muuttujaan. Viimeinen rivi kutsuu muuttujan sisältämää funktiota, joka aktivoi tallennetun koodin (paluunumero * tekijä;). Sillä on edelleen pääsy kerroinmuuttujaan, joka määriteltiin kertojaa kutsuttaessa, ja sillä on myös pääsy unfreeze (5) -vaiheessa välitettyyn argumenttiin numeerisena parametrina.

rekursio

Funktio voi hyvinkin kutsua itseään, jos se huolehtii, ettei se vuoda pinon yli. Tällaista funktiota kutsutaan rekursiiviseksi. Tässä on esimerkki vaihtoehtoisesta eksponentioinnista:

Funktio potenssi(kanta, eksponentti) ( jos (eksponentti == 0) palauttaa 1; muuten palauttaa kanta * potenssi(kanta, eksponentti - 1); ) console.log(teho(2, 3)); // → 8

Näin matemaatikot määrittelevät eksponentioinnin, ja ehkä tämä kuvaa käsitettä tyylikkäämmin kuin sykli. Funktio kutsuu itseään useita kertoja eri argumenteilla saavuttaakseen moninkertaisen kertolaskun.

Tässä toteutuksessa on kuitenkin ongelma - normaalissa JavaScript-ympäristössä se on 10 kertaa hitaampi kuin silmukalla varustettu versio. Silmukointi on halvempaa kuin funktion kutsuminen.

Nopeus vs. eleganssi dilemma on varsin mielenkiintoinen. Ihmisten mukavuuden ja koneen mukavuuden välillä on tietty kuilu. Mitä tahansa ohjelmaa voidaan nopeuttaa tekemällä siitä isompi ja monimutkaisempi. Ohjelmoijan on löydettävä oikea tasapaino.

Ensimmäisen eksponentioinnin tapauksessa epäelegantti silmukka on melko yksinkertainen ja suoraviivainen. Ei ole järkevää korvata sitä rekursiolla. Usein ohjelmat toimivat kuitenkin niin monimutkaisilla käsitteillä, että tehokkuutta halutaan vähentää lisäämällä luettavuutta.

Perussääntö, joka on toistettu monta kertaa ja jonka kanssa olen täysin samaa mieltä - älä ole huolissasi suorituskyvystä ennen kuin olet varma, että ohjelma hidastuu. Jos näin on, etsi osat, jotka kestävät pisimpään ja vaihda eleganssia tehokkuuteen siellä.

Emme tietenkään saisi heti täysin sivuuttaa suorituskykyä. Monissa tapauksissa, kuten eksponentioinnissa, emme saa paljon yksinkertaisuutta tyylikkäistä ratkaisuista. Joskus kokenut ohjelmoija huomaa heti, että yksinkertainen lähestymistapa ei koskaan ole tarpeeksi nopea.

Otan tämän esille, koska liian monet aloittelevat ohjelmoijat pitävät kiinni tehokkuudesta pienissäkin asioissa. Tulos on suurempi, monimutkaisempi ja usein virheetön. Tällaisten ohjelmien kirjoittaminen kestää kauemmin, eivätkä ne usein toimi paljon nopeammin.

Mutta rekursio ei aina ole vain vähemmän tehokas vaihtoehto silmukoille. Jotkut ongelmat on helpompi ratkaista rekursiolla. Useimmiten tämä on useiden puun oksien läpikulku, joista jokainen voi haarautua.

Tässä on sinulle arvoitus: voit saada äärettömän määrän numeroita alkaen numerosta 1 ja sitten joko lisäämällä 5 tai kertomalla 3:lla. Kuinka kirjoitetaan funktio, joka yrittää löytää luvun annettuna sarjan tällaisia yhteen- ja kertolaskuja, jotka johtavat tiettyyn numeroon? Esimerkiksi luku 13 voidaan saada kertomalla ensin 1 kolmella ja lisäämällä sitten 5 kahdesti. Ja numeroa 15 on yleensä mahdotonta saada sellaiseksi.

Rekursiivinen ratkaisu:

Funktio findSolution(target) ( funktio find(aloitus, historia) ( if (aloitus == kohde) paluuhistoria; else if (aloitus > kohde) return null; else return find(aloitus + 5, "(" + historia + " + 5)") || find(aloitus * 3, "(" + historia + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Tämä esimerkki ei välttämättä löydä lyhintä ratkaisua - se tyydyttää minkä tahansa. En odota sinun ymmärtävän heti, miten ohjelma toimii. Mutta mennään tämän suuren rekursiivisen ajattelun harjoituksen pohjaan.

Sisäinen funktiohaku on rekursiivinen. Se vaatii kaksi argumenttia - nykyisen luvun ja merkkijonon, joka sisältää tietueen siitä, kuinka olemme päässeet kyseiseen numeroon. Ja palauttaa joko merkkijonon, joka näyttää vaihesarjamme, tai nollan.

Tätä varten toiminto suorittaa yhden kolmesta toiminnosta. Jos annettu luku on yhtä suuri kuin tavoite, niin nykyinen historia on juuri tapa saavuttaa se, minkä vuoksi se palautetaan. Jos annettu luku on suurempi kuin tavoite, kerto- ja yhteenlaskua ei kannata jatkaa, sillä näin se vain kasvaa. Ja jos emme ole vielä saavuttaneet tavoitetta, funktio kokeilee molempia mahdollisia polkuja annetusta numerosta alkaen. Hän kutsuu itsensä kahdesti, kerran kummallakin tavalla. Jos ensimmäinen puhelu ei palaa tyhjäksi, se palaa. Muussa tapauksessa toinen palautetaan.

Ymmärtääksemme paremmin, kuinka funktio saavuttaa halutun vaikutuksen, tarkastellaan sen kutsuja, jotka tapahtuvat etsiessään ratkaisua numeroon 13.

Etsi(1, "1") etsi(6, "(1 + 5)") etsi(11, "((1 + 5) + 5)") etsi(16, "((1 + 5) + 5 ) + 5)") liian suuri löytö(33, "((1 + 5) + 5) * 3)") liian suuri löytö(18, "((1 + 5) * 3)") liian suuri löytö( 3, "(1 * 3)") etsi(8, "((1 * 3) + 5)") etsi(13, "((1 * 3) + 5) + 5)") löydetty!

Sisennys näyttää puhelupinon syvyyden. Ensimmäisellä kerralla hakutoiminto kutsuu itseään kahdesti tarkistaakseen (1 + 5) ja (1 * 3) alkavat ratkaisut. Ensimmäinen kutsu etsii ratkaisua, joka alkaa (1 + 5) ja tarkistaa rekursion avulla kaikki ratkaisut, jotka tuottavat luvun, joka on pienempi tai yhtä suuri kuin haluttu luku. Ei löydä sitä ja palauttaa nollan. Sitten operaattori || ja hyppää toimintokutsuun, joka tutkii vaihtoehtoa (1 * 3). Tässä olemme onnekkaita, sillä kolmannessa rekursiivisessa kutsussa saamme 13. Tämä puhelu palauttaa merkkijonon, ja jokainen || ohittaa tämän merkkijonon matkan varrella ja palauttaa tuloksena ratkaisun.

Kasvata toiminnot

On kaksi enemmän tai vähemmän luonnollista tapaa tuoda funktioita ohjelmaan.

Ensin kirjoitat samanlaisen koodin useita kertoja. Tätä tulisi välttää - enemmän koodia tarkoittaa enemmän tilaa virheille ja enemmän luettavaa niille, jotka yrittävät ymmärtää ohjelmaa. Otamme siis toistuvan toiminnallisuuden, annamme sille hyvän nimen ja laitamme sen funktioksi.

Toinen tapa on, että huomaat tarpeen uudelle toiminnallisuudelle, joka kannattaa sijoittaa erilliseen toimintoon. Aloitat funktion nimellä ja kirjoitat sitten sen rungon. Voit jopa aloittaa kirjoittamalla funktiota käyttävän koodin ennen kuin itse funktio on määritetty.

Se, kuinka vaikeaa sinun on nimetä toiminto, osoittaa, kuinka hyvin ymmärrät sen toiminnallisuuden. Otetaan esimerkki. Meidän on kirjoitettava ohjelma, joka tulostaa kaksi numeroa, tilalla olevien lehmien ja kanojen lukumäärän, jota seuraa sanat "lehmät" ja "kanat". Sinun on lisättävä nollia edessä oleviin numeroihin niin, että jokainen on täsmälleen kolmessa paikassa.

007 Lehmät 011 Kanat

Ilmeisesti tarvitsemme funktion, jossa on kaksi argumenttia. Aloitetaan koodaaminen.
// tulostaFarmInventory-funktio printFarmInventory(lehmät, kanat) ( var cowString = String(lehmät); while (cowString.length< 3) cowString = "0" + cowString; console.log(cowString + " Коров"); var chickenString = String(chickens); while (chickenString.length < 3) chickenString = "0" + chickenString; console.log(chickenString + " Куриц"); } printFarmInventory(7, 11);

Jos lisäämme merkkijonoon .length, saamme sen pituuden. Osoittautuu, että while-silmukat lisäävät numeroihin etunollia, kunnes ne saavat 3 merkin pituisen merkkijonon.

Valmis! Mutta heti kun aiomme lähettää koodin viljelijälle (mukaan lukien tietysti iso sekki), hän soittaa ja kertoo, että hänellä on sikoja tilalla ja voisimmeko lisätä sikojen lukumäärän. ohjelmaan?

Tietysti se on mahdollista. Mutta kun alamme kopioida ja liittää koodia näiltä neljältä riviltä, ​​ymmärrämme, että meidän täytyy pysähtyä ja ajatella. Täytyy olla parempi tapa. Yritämme parantaa ohjelmaa:

// outputZeroPaddedWithLabel-funktio printZeroPaddedWithLabel(number, label) ( var numberString = Merkkijono(numero); while (numberString.length< 3) numberString = "0" + numberString; console.log(numberString + " " + label); } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { printZeroPaddedWithLabel(cows, "Коров"); printZeroPaddedWithLabel(chickens, "Куриц"); printZeroPaddedWithLabel(pigs, "Свиней"); } printFarmInventory(7, 11, 3);

Toimii! Mutta nimi printZeroPaddedWithLabel on hieman outo. Se yhdistää kolme asiaa - tulosteen, nollapehmusteen ja etiketin - yhdeksi toiminnoksi. Sen sijaan, että täyttäisimme koko toistuvan fragmentin funktioksi, korostetaan yksi käsite:

// lisää nolliafunktio zeroPad(numero, leveys) ( var string = String(numero); while (merkkijono.length< width) string = "0" + string; return string; } // вывестиИнвентаризациюФермы function printFarmInventory(cows, chickens, pigs) { console.log(zeroPad(cows, 3) + " Коров"); console.log(zeroPad(chickens, 3) + " Куриц"); console.log(zeroPad(pigs, 3) + " Свиней"); } printFarmInventory(7, 16, 3);

Funktio, jolla on mukava, kuvaava nimi zeroPad, tekee koodista helpompi ymmärtää. Ja sitä voidaan käyttää monissa tilanteissa, ei vain meidän tapauksessamme. Esimerkiksi muotoiltujen taulukoiden näyttämiseen numeroineen.

Kuinka älykkäitä ja monipuolisia toimintojen tulee olla? Voimme kirjoittaa yksinkertaisen funktion, joka täyttää luvun nolilla enintään kolmeen paikkaan, sekä hienon yleiskäyttöisen funktion lukujen muotoiluun, joka tukee murtolukuja, negatiivisia lukuja, pisteiden tasausta, eri merkkien täyttöä ja niin edelleen.

Hyvä nyrkkisääntö on lisätä vain ne toiminnot, joita ehdottomasti tarvitset. Joskus on houkuttelevaa luoda yleiskäyttöisiä puitteita jokaiseen pieneen tarpeeseen. Vastusta häntä. Et koskaan lopeta työtä, vaan kirjoita vain joukko koodia, jota kukaan ei käytä.

Toiminnot ja sivuvaikutukset

Toiminnot voidaan karkeasti jakaa niihin, joita kutsutaan niiden sivuvaikutusten vuoksi, ja niihin, joita kutsutaan saadakseen jotakin arvoa. Tietenkin on myös mahdollista yhdistää nämä ominaisuudet yhdeksi funktioksi.

Maatilaesimerkin ensimmäinen aputoiminto, printZeroPaddedWithLabel, kutsutaan merkkijonon tulostamisen sivuvaikutuksen vuoksi. Toinen, zeroPad, palautusarvon takia. Eikä ole sattumaa, että toinen ominaisuus on hyödyllinen useammin kuin ensimmäinen. Arvoja palauttavia toimintoja on helpompi yhdistää toisiinsa kuin sivuvaikutuksia aiheuttavia toimintoja.

Puhdas funktio on erityinen arvon palauttava funktio, jolla ei vain ole sivuvaikutuksia, mutta se ei myöskään ole riippuvainen muun koodin sivuvaikutuksista - ei esimerkiksi toimi globaalien muuttujien kanssa, joita voidaan muuttaa vahingossa jossain muualla. Puhdas funktio, kun sitä kutsutaan samoilla argumenteilla, palauttaa saman tuloksen (eikä tee mitään muuta) - mikä on melko mukavaa. Hänen kanssaan on helppoa työskennellä. Tällaisen funktion kutsu voidaan henkisesti korvata sen työn tuloksella muuttamatta koodin merkitystä. Kun haluat testata tällaista toimintoa, voit kutsua sitä ja olla varma, että jos se toimii tässä yhteydessä, se toimii missä tahansa. Ei niin puhtaat funktiot voivat tuottaa erilaisia ​​tuloksia monista tekijöistä riippuen, ja niillä voi olla sivuvaikutuksia, joita on vaikea testata ja ottaa huomioon.

Ei kuitenkaan pidä ujostella funktioita, jotka eivät ole aivan puhtaita, tai aloittaa koodin pyhää puhdistusta sellaisista funktioista. Sivuvaikutukset ovat usein hyödyllisiä. Console.log-funktiosta ei voi kirjoittaa puhdasta versiota, ja tämä toiminto on varsin hyödyllinen. Jotkut toiminnot on helpompi ilmaista sivuvaikutusten avulla.

Tulokset

Tämä luku osoitti, kuinka voit kirjoittaa omia funktioita. Kun funktion avainsanaa käytetään lausekkeena, se palauttaa osoittimen funktiokutsuun. Käskynä käytettynä voit ilmoittaa muuttujan määrittämällä sille funktiokutsun.

Avain toimintojen ymmärtämiseen on paikalliset laajuudet. Funktion sisällä ilmoitetut parametrit ja muuttujat ovat sille paikallisia, luodaan uudelleen joka kerta, kun sitä kutsutaan, eivätkä ne näy ulkopuolelta. Toisen funktion sisällä ilmoitetuilla funktioilla on pääsy sen soveltamisalaan.

On erittäin hyödyllistä jakaa ohjelman suorittamat eri tehtävät funktioihin. Sinun ei tarvitse toistaa itseäsi, funktiot tekevät koodista luettavamman erottelemalla sen semanttisiin osiin, samalla tavalla kuin kirjan luvut ja osat auttavat järjestämään pelkkää tekstiä.

Harjoitukset

Minimi
Edellisessä luvussa mainittiin Math.min-funktio, joka palauttaa pienimmän argumenttinsa. Nyt voimme kirjoittaa sellaisen funktion itse. Kirjoittaa toiminto min A, joka ottaa kaksi argumenttia ja palauttaa niistä pienimmän.

Console.log(min(0, 10)); // → 0 konsoli.log(min(0, -10)); // → -10

rekursio
Olemme nähneet, että % (jäännös) -operaattorilla voidaan määrittää, onko luku parillinen (% 2). Tässä on toinen tapa määrittää:

Nolla on parillinen.
Yksikkö on outo.
Millä tahansa luvulla N on sama pariteetti kuin N-2:lla.

Kirjoita rekursiivinen isEven-funktio näiden sääntöjen mukaan. Sen on otettava luku ja palautettava looginen arvo.

Testaa sitä arvoilla 50 ja 75. Anna sille -1. Miksi hän käyttäytyy näin? Onko mahdollista korjata se jotenkin?

Testaa sitä 50:llä ja 75:llä. Katso, kuinka se toimii -1:llä. Miksi? Voitko mietitkö tapaa korjata tämä?

Console.log(isEven(50)); // → true console.log(isEven(75)); // → false console.log(isEven(-1)); // → ??

Me laskemme pavut.

Merkkijonon merkkinumero N saadaan lisäämällä siihen .charAt(N)("string".charAt(5)), samalla tavalla kuin .length-merkkijonon pituus. Palautusarvo on yksi merkkijono (esimerkiksi "k"). Merkkijonon ensimmäisen merkin paikka on 0, mikä tarkoittaa sitä viimeinen hahmo sijainti on string.length - 1. Toisin sanoen kaksimerkkisen merkkijonon pituus on 2 ja sen merkkipaikat ovat 0 ja 1.

Kirjoita countBs-funktio, joka ottaa merkkijonon argumenttina ja palauttaa "B"-merkkien määrän merkkijonossa.

Kirjoita sitten countChar-funktio, joka toimii vähän kuin countBs, paitsi että se vaatii toisen parametrin, merkkijonosta etsimän merkin (sen sijaan, että laskettaisiin vain "B"-merkkien lukumäärä). Voit tehdä tämän kirjoittamalla countBs-funktion uudelleen.

Jump-lauseet ja poikkeusten käsittely

Toinen JavaScript-kielen operaattorien luokka ovat hyppyoperaattorit. Kuten nimestä voi päätellä, nämä lauseet saavat JavaScript-tulkin hyppäämään eri paikkaan ohjelmakoodissa. Break-lause saa tulkin hyppäämään silmukan tai muun käskyn loppuun. Jatka-lause saa tulkin ohittamaan loput silmukan rungosta, hyppäämään takaisin silmukan alkuun ja aloittamaan uuden iteroinnin. JavaScriptillä on kyky merkitä lauseisiin nimiä, jotta katkaisu- ja jatkalausekkeet voidaan nimenomaisesti ilmoittaa, mihin silmukkaan tai muuhun lauseeseen ne kuuluvat.

Return-lause saa tulkin hyppäämään kutsutusta funktiosta takaisin kohtaan, jossa sitä kutsuttiin, ja palauttamaan kutsun arvon. heitto-käsky herättää poikkeuksen ja on suunniteltu toimimaan yhdessä try/catch/finally-lausekkeiden kanssa, jotka määrittelevät koodilohkon poikkeuksen käsittelemiseksi. Tämä on melko monimutkainen hyppykäsky: kun poikkeus tapahtuu, tulkki hyppää lähimpään sulkevaan poikkeuskäsittelijään, joka voi olla samassa tai korkeammassa funktiossa kutsutun funktion paluupinossa.

Jokainen näistä hyppyoperaattoreista on kuvattu yksityiskohtaisemmin seuraavissa alaosissa.

Ohjeet etiketit

Mikä tahansa lausunto voidaan merkitä tunnisteella ja kaksoispisteellä ennen sitä:

tunniste: ohje

Kun merkitset ohjeen, annat sille nimen, jota voidaan sitten käyttää viitteenä missä tahansa ohjelmassa. Voit merkitä minkä tahansa käskyn, mutta on järkevää merkitä vain käskyt, joilla on runko, kuten silmukat ja ehdolliset lauseet.

Kun silmukalle annetaan nimi, sitä voidaan sitten käyttää katkeaa ja jatka-lauseissa, silmukan sisällä poistuakseen silmukasta tai siirtyäkseen silmukan alkuun, seuraavaan iteraatioon. Katko- ja jatka-lauseet ovat ainoita JavaScript-kielen lausekkeita, jotka voivat sisältää tunnisteita – niitä käsitellään tarkemmin myöhemmin. Seuraavassa on esimerkki while-lauseesta, jossa on otsikko ja jatka-lauseke, joka käyttää tätä tunnistetta:

Mainloop: while (token != null) ( // Ohjelmakoodi jätetty pois... jatka pääsilmukkaa; // Siirry nimetyn silmukan seuraavaan iteraatioon )

Lauseketunnisteena käytettävä tunniste voi olla mikä tahansa kelvollinen JavaScript-tunniste, paitsi varattu sana. Tunnisteiden nimet ovat erillisiä muuttujien ja funktioiden nimistä, joten voit käyttää tunnisteita, jotka vastaavat muuttujien tai funktioiden nimiä.

Ohjetarrat määritellään vain ohjeissa, joita ne koskevat (ja tietysti niihin sisäkkäisissä ohjeissa). Sisäkkäisiä ohjeita ei voi merkitä samoilla tunnisteilla kuin ne sisältävät ohjeet, mutta kaksi erillistä ohjetta voidaan merkitä samalla tunnisteella. Merkityt ohjeet voidaan merkitä uudelleen. Eli missä tahansa ohjeessa voi olla useita tarroja.

katkeaa lausunto

Break-käsky saa sisimmän silmukan tai kytkinkäskyn poistumaan välittömästi. Olemme jo nähneet esimerkkejä break-lauseen käyttämisestä kytkinkäskyn sisällä. Silmukoissa sitä käytetään yleensä välittömästi poistumaan silmukasta, kun jostain syystä silmukan suorittaminen on tarpeen lopettaa.

Kun kierto on hyvin monimutkainen tila loppuun, on usein helpompi toteuttaa nämä ehdot break-lauseella kuin yrittää ilmaista ne yhdellä silmukalla. Seuraava esimerkki yrittää löytää taulukkoelementin tietyllä arvolla. Silmukka päättyy tavalliseen tapaan, kun taulukon loppu saavutetaan, tai break-lauseella heti, kun haluttu arvo löytyy:

Var arr = ["a","b","c","d","e"], tulos; for (var i = 0; i

JavaScriptissä voit määrittää tunnisteen nimen break-avainsanan jälkeen (tunniste ilman kaksoispistettä):

katkaista etiketin_nimi;

Kun break-lausetta käytetään tunnisteen kanssa, se hyppää nimetyn käskyn loppuun tai lopettaa sen suorittamisen. Jos määritetyllä tunnisteella varustettua käskyä ei ole, yritys käyttää tätä break-lauseen muotoa tuottaa syntaksivirheen. Nimetyn käskyn ei tarvitse olla silmukka- tai kytkinkäsky. Merkitty break-lause voi "paeta" mistä tahansa sisältävästä lauseesta. Täydentävä lauseke voi olla jopa yksinkertainen lausekelohko, joka on suljettu aaltosulkeisiin, joiden ainoa tarkoitus on merkitä se.

Rivinvaihtomerkkiä ei voi lisätä break-avainsanan ja tunnisteen nimen väliin. Tämä johtuu siitä, että JavaScript-tulkki lisää automaattisesti puuttuvat puolipisteet: jos katkaiset koodirivin break-avainsanan ja sitä seuraavan otsikon väliin, tulkki olettaa, että tarkoitit tämän operaattorin yksinkertaista muotoa ilman tunnistetta ja lisää puolipisteen. .

Merkitty break-lause vaaditaan vain, kun haluat katkaista sellaisen käskyn suorituksen, joka ei ole lähin sulkeva silmukka tai kytkinkäsky.

jatka lausuntoa

Jatka-lause on samanlainen kuin break-lause. Silmukasta poistumisen sijaan jatka-käsky aloittaa silmukan uuden iteraation. Continue-lauseen syntaksi on yhtä yksinkertainen kuin break-lauseen syntaksi. Jatka-lausetta voidaan käyttää myös etiketin kanssa.

Jatka-lausetta, olipa se nimeämätön tai merkitty, voidaan käyttää vain silmukan rungossa. Sen käyttäminen missä tahansa muualla johtaa syntaksivirheeseen. Kun jatka-käsky suoritetaan, silmukan nykyinen iteraatio keskeytyy ja seuraava alkaa. varten erilaisia ​​tyyppejä syklit tarkoittavat eri asioita:

    While-silmukassa silmukan alussa määritetty lauseke tarkistetaan uudelleen, ja jos se on tosi, silmukan runko suoritetaan alusta alkaen.

    Do/while-silmukka hyppää silmukan loppuun, jossa ehto tarkistetaan uudelleen ennen silmukan toistamista.

    For-silmukassa lisäyslauseke arvioidaan ja testilauseke arvioidaan uudelleen sen määrittämiseksi, pitäisikö seuraava iteraatio suorittaa.

    For/in-silmukassa silmukka alkaa alusta ja antaa määritetylle muuttujalle seuraavan ominaisuuden nimen.

Huomaa ero jatkaa-lauseen käyttäytymisessä while- ja for-silmukoissa. While-silmukka palaa suoraan ehtoonsa, kun taas for-silmukka arvioi ensin lisäyslausekkeen ja palaa sitten ehtoon. Seuraava esimerkki esittää nimeämättömän jatkamiskäskyn käytön parillisten lukujen silmukan nykyisestä iteraatiosta poistumiseen:

var summa = 0; // Laske parittomien lukujen summa 0-10 arvolle (muut i = 0; i

Continue-lausetta, kuten break, voidaan käyttää sisäkkäisissä silmukoissa tunnisteen sisältävässä muodossa, jolloin uudelleen aloitettu silmukka ei välttämättä ole se, joka sisältää välittömästi jatkamiskäskyn. Kuten tauon kohdalla, rivinvaihdot jatka-avainsanan ja tunnisteen nimen välillä eivät ole sallittuja.

palautusilmoitus

Funktiokutsu on lauseke, ja kuten kaikilla lausekkeilla, sillä on arvo. Funktioiden sisällä olevaa return-lausetta käytetään määrittämään funktion palauttama arvo. Return-lause voidaan sijoittaa vain funktion runkoon. Sen esiintyminen muualla on syntaksivirhe. Kun return-käsky suoritetaan, funktio palauttaa lausekkeen arvon kutsuvalle ohjelmalle. Esimerkiksi:

Jos funktiolla ei ole return-käskyä, tulkki suorittaa sitä kutsuttaessa funktion rungossa olevat ohjeet yksitellen, kunnes se saavuttaa funktion loppuun, ja palauttaa sitten ohjauksen sen kutsuneelle ohjelmalle. Tässä tapauksessa kutsulauseke palauttaa määrittelemättömän. Palautusilmoitus on usein viimeinen ohje funktiossa, mutta tämä on täysin valinnainen: funktio palauttaa ohjauksen kutsuvalle ohjelmalle heti, kun return-käsky on saavutettu, vaikka sitä seuraisi muita funktion rungon käskyjä.

Return-lausetta voidaan käyttää myös ilman lauseketta, jolloin se yksinkertaisesti keskeyttää funktion ja palauttaa kutsujalle määrittelemättömän. Esimerkiksi:

Funktio myFun(arr) ( // Jos taulukko sisältää negatiivisia lukuja, keskeytä funktio (var i = 0; i

heittää lausunto

Poikkeus on signaali, joka osoittaa jonkinlaisen poikkeuksen tai virheen. Poikkeuksen esittäminen (heitto) on tapa ilmoittaa tällaisesta virheestä tai poikkeuksesta. Poikkeuksen kiinni saaminen (catch) tarkoittaa sen käsittelemistä, ts. ryhtyä tarpeellisiin tai aiheellisiin toimiin poikkeuksesta toipumiseksi.

JavaScriptissä poikkeukset heitetään, kun tapahtuu ajonaikainen virhe ja kun ohjelma nimenomaisesti nostaa sen throw-käskyllä. Poikkeukset saadaan kiinni käyttämällä try/catch/finally-lauseita, jotka kuvataan myöhemmin.

Heittolauseella on seuraava syntaksi:

heittää ilme;

Lausekkeen tulos voi olla minkä tahansa tyyppinen arvo. Heittolause voidaan välittää numero, joka edustaa virhekoodia, tai merkkijono, joka sisältää virheilmoituksen tekstin. JavaScript-tulkki heittää poikkeuksia luokan esiintymän avulla virhe yksi sen alaluokista, ja voit myös käyttää samanlaista lähestymistapaa. Error-objektilla on ominaisuus nimi, joka määrittää virhetyypin ja ominaisuuden viesti A, joka sisältää konstruktorifunktiolle välitetyn merkkijonon. Seuraavassa on esimerkki funktiosta, joka herättää Error-objektin, kun sitä kutsutaan virheellisellä argumentilla:

// Lukufunktion funktiofaktoriaali factorial(number) ( // Jos syöteargumentti ei ole kelvollinen arvo, // heitetään poikkeus! if (numero 1; i *= numero, numero--); /* tyhjä loop body */ return i ; ) konsoli log("5! = ", factorial(5)); konsoli. log("-3! = ", factorial(-3));

Kun poikkeus heitetään, JavaScript-tulkki keskeyttää välittömästi ohjelman normaalin suorituksen ja hyppää lähimpään poikkeuskäsittelijään. Poikkeuskäsittelijät käyttävät try/catch/finally-konstruktin catch-lausetta, joka kuvataan seuraavassa osiossa.

Jos koodilohkolla, jossa poikkeus tapahtui, ei ole vastaavaa catch-rakennetta, tulkki jäsentää seuraavan ulomman koodilohkon ja tarkistaa, onko siihen liitetty poikkeuskäsittelijä. Tämä jatkuu, kunnes ohjaaja löytyy.

Jos poikkeus heitetään funktioon, joka ei sisällä try/catch/finally-konstruktiota sen käsittelemiseksi, poikkeus etenee funktiota kutsuvaan koodiin. Näin poikkeukset etenevät JavaScript-menetelmien leksikaalisen rakenteen kautta kutsupinossa. Jos poikkeuskäsittelijää ei koskaan löydy, poikkeusta käsitellään virheenä ja raportoidaan käyttäjälle.

kokeile / saa kiinni / vihdoin rakentaa

Try/catch/finally -konstruktio toteuttaa JavaScriptin poikkeusten käsittelymekanismin. kokeile lausuntoa tässä konstruktiossa yksinkertaisesti määrittelee koodilohkon, jossa poikkeuksia käsitellään. Yritä-lohkoa seuraa saalis lausunto lauselohkolla, joka kutsutaan, jos poikkeus tapahtuu missä tahansa try-lohkossa. Cat-lausetta seuraa lohko vihdoinkin A, joka sisältää koodin, joka suorittaa lopulliset toiminnot ja joka on taatusti suoritettava riippumatta siitä, mitä try-lohkossa tapahtuu.

Sekä catch-lohko että lopullinen lohko ovat valinnaisia, mutta vähintään yhden niistä on oltava läsnä try-lohkon jälkeen. yritä, saa kiinni ja lopuksi lohkot alkavat ja päättyvät aaltosulkeet. Tämä on pakollinen osa syntaksia, eikä sitä voi jättää pois, vaikka niiden välillä olisi vain yksi lause.

Seuraava katkelma havainnollistaa try/catch/finally -konstruktin syntaksia ja tarkoitusta:

Kokeile ( // Normaalisti tämä koodi toimii sujuvasti alusta loppuun. // Mutta jossain vaiheessa se saattaa heittää poikkeuksen, // joko suoraan throw-käskyn kanssa tai epäsuorasti, // kutsumalla menetelmää, joka heittää poikkeus. ) catch (ex) ( // Tämän lohkon käskyt suoritetaan, jos ja vain jos try-lohkossa tapahtuu poikkeus //. Nämä lauseet voivat käyttää paikallista muuttujaa ex, joka // viittaa Error-objektiin tai johonkin muuhun heittokäskyssä määritetty arvo. // Tämä lohko voi joko käsitellä poikkeusta jollain tavalla tai // jättää sen huomiotta ja tehdä jotain muuta, tai // heittää poikkeuksen uudelleen throw-käskyllä. ) Lopuksi ( // Tämä lohko sisältää käskyt jotka suoritetaan aina riippumatta siitä onko , // mitä try-lohkossa tapahtui. Ne suoritetaan, jos try-lohko päättyi: // 1) tavalliseen tapaan saavuttaen lohkon loppuun // 2) katkeamisen, jatkamisen tai paluujakson takia lausunnot // 3) poikkeuksella, jota käsittelee yllä olevassa saalislohkossa // 4) lukuunottamatta jäänyt poikkeus, joka jatkaa // etenemistä korkeammalle tasolle)

Huomaa, että catch-avainsanan jälkeen on tunniste suluissa. Tämä tunniste on samanlainen kuin funktioparametri. Kun poikkeus havaitaan, tämä parametri asetetaan poikkeukseksi (esimerkiksi Error-objekti). Toisin kuin normaali muuttuja, catch-lauseeseen liittyvä tunniste on olemassa vain catch-lohkon rungossa.

Seuraava on realistisempi esimerkki try/catch-rakenteesta. Se kutsuu edellisessä esimerkissä määritettyä factorial()-metodia ja asiakaspuolen JavaScriptin prompt()- ja alert()-menetelmiä syötteen ja tulosteen järjestämiseksi:

Kokeile ( // Pyydä käyttäjältä lukua var n = Number(prompt("Syötä positiivinen luku", "")); // Laske luvun tekijä, jos // syöte on kelvollinen var f = factorial( n); // Tulosta tulos alert(n + "! = " + f); ) catch (ex) ( // Jos tiedot ovat virheellisiä, ohjaus siirretään tähän alert(ex); // Ilmoita käyttäjälle virhe )

Jos käyttäjä syöttää negatiivisen luvun, näyttöön tulee varoitusviesti:

Tämä on esimerkki try/catch-konstruktista ilman lopullista lauseketta. Vaikka vihdoin ei käytetä niin usein kuin catch, se on kuitenkin joskus hyödyllinen. Lopullinen lohko taataan toteutuvan, jos ainakin osa try-lohkosta on suoritettu, riippumatta siitä, miten try-lohkon koodi päättyi. Tätä ominaisuutta käytetään yleensä suorittamaan viimeiset toiminnot sen jälkeen, kun koodi on suoritettu kokeilujatkossa.

Normaalitilanteessa ohjaus saavuttaa try-lohkon loppuun ja sitten hyppää viimeiseen lohkoon, joka suorittaa tarvittavat lopputoiminnot. Jos ohjaus poistuu try-lohkosta return-, jatka- tai break-käskyn seurauksena, lopullinen lohko suoritetaan ennen kuin ohjaus siirretään muualle.

Jos try-lohkossa tapahtuu poikkeus ja sen käsittelemiseen on sopiva catch-lohko, ohjaus siirretään ensin catch-lohkoon ja sitten viimeiseen lohkoon. Jos paikallista kiinnityslohkoa ei ole, ohjaus siirtyy ensin viimeiseen lohkoon ja sitten hyppää lähimpään ulompaan kiinnityslohkoon, joka voi käsitellä poikkeuksen.

Jos lopullinen lohko itse siirtää ohjauksen paluu-, jatka-, tauko- tai heitto-käskyllä ​​tai kutsumalla poikkeuksen heittävää metodia, odottava siirtokomento peruuntuu ja uusi suoritetaan. Jos esimerkiksi lopullinen lohko heittää poikkeuksen, tämä poikkeus korvaa minkä tahansa aiemmin heitetyn poikkeuksen.

Operaattori palata lopettaa nykyisen funktion ja palauttaa sen arvon.

Tämän interaktiivisen esimerkin lähdekoodi on tallennettu GitHub-tietovarastoon. Jos haluat osallistua interaktiivisten esimerkkien projektiin, kloonaa https://github.com/mdn/interactive-examples

Syntaksi

palauttaa [[lauseke]]; lauseke Lauseke, jonka arvo palautetaan. Jos sitä ei ole määritetty, sen sijaan palautetaan undefined.

Kuvaus

Kun funktiossa kutsutaan return-käskyä, sen suoritus pysähtyy. Määritetty arvo palautetaan paikkaan, jossa funktiota kutsuttiin. Esimerkiksi seuraava funktio palauttaa argumenttinsa x neliön arvon (jossa x on luku):

funktio neliö(x) ( paluu x * x; ) var demo = neliö(3); // demon arvo on 9

Jos palautusarvoa ei ole määritetty, sen sijaan palautetaan undefined.

Seuraavat lausekkeet lopettavat aina funktion suorittamisen:

palata; palauttaa tosi; palauttaa väärä; palauttaa x; paluu x + y / 3;

Automaattiset puolipisteet

function magic(x) ( return function calc(x) ( return x * 42 ); ) var vastaus = magic(); vastaus(1337); // 56154

Tekniset tiedot

Erittely Tila Kommentti
ECMAScript 1st Edition (ECMA-262) Vakio alkuperäinen määritelmä
ECMAScript 5.1 (ECMA-262)
Vakio
ECMAScript 2015 (6. painos, ECMA-262)
"Palautuslausekkeen" määritelmä tässä eritelmässä.
Vakio
ECMAScriptin uusin luonnos (ECMA-262)
"Palautuslausekkeen" määritelmä tässä eritelmässä.
Luonnos

Selaimen yhteensopivuus

Tämän sivun yhteensopivuustaulukko on luotu strukturoiduista tiedoista. Jos haluat osallistua tietoihin, tarkista se https://github.com/mdn/browser-compat-data-tietovarastosta ja lähetä meille muutospyyntösi.

Päivitä GitHubin yhteensopivuustiedot

Tietokoneetmobiilipalvelin
KromireunaFirefoxInternet ExplorerOopperasafariandroid webviewChrome AndroidilleFirefox AndroidilleOpera AndroidilleSafari iOS:ssäSamsung InternetNode.js
palataKromi Täysi tuki 1 reuna Täysi tuki 12 Firefox Täysi tuki 1 IE Täysi tuki 3 Ooppera Täysi tuki Joosafari Täysi tuki Joowebview android Täysi tuki 1 Chrome Android Täysi tuki 18 Firefox Android Täysi tuki 4 OperaAndroid Täysi tuki JooSafari iOS Täysi tuki JooSamsung Internet Android Täysi tuki 1.0 nodejs Täysi tuki Joo

Funktiot ovat yksi tärkeimmistä JavaScriptin koodin rakennuspalikoista.

Funktiot koostuvat joukosta komentoja ja suorittavat yleensä yhden tietyn tehtävän (esimerkiksi lukujen summaaminen, juuren laskeminen jne.).

Funktioon sijoitettu koodi suoritetaan vasta sen jälkeen, kun kyseiselle funktiolle on annettu nimenomainen kutsu.

Toiminnan ilmoitus

1.Syntaksi:

//Funktion määritysfunktio FunctionName(muuttuja1, muuttuja2)( Funktiokoodi ) //Funktion kutsu FunctionName(muuttuja1,muuttuja2);

2. Syntaksi:

//Funktion määritys var funktionnimi=funktio(muuttuja1, muuttuja2)(funktiokoodi) //Funktion kutsu funktionimi(muuttuja1,muuttuja2);

funktion nimi määrittää funktion nimen. Jokaisella sivun funktiolla on oltava yksilöllinen nimi. Toiminnon nimi on annettava latinalaisilla kirjaimilla eikä se saa alkaa numeroilla.

per1 Ja per2 ovat muuttujia tai arvoja, jotka voidaan välittää funktion sisällä. Kullekin funktiolle voidaan välittää rajoittamaton määrä muuttujia.

Huomautus: vaikka funktiolle ei välitetä muuttujia, älä unohda lisätä sulkuja "()" funktion nimen perään.

Huomautus: JavaScriptin funktioiden nimet ovat kirjainkoolla.

Esimerkki JavaScript-funktiosta

Alla olevan esimerkin messageWrite()-funktio suoritetaan vasta painikkeen napsautuksen jälkeen.

Huomautus: tässä esimerkissä käytetään onclick-tapahtumaa. JavaScript-tapahtumia käsitellään yksityiskohtaisesti myöhemmin tässä opetusohjelmassa.



Muuttujien välittäminen funktioille

Voit välittää funktioille rajattoman määrän muuttujia.

Huomautus: kaikki funktioiden sisällä olevien muuttujien käsittelyt eivät itse asiassa suoriteta itse muuttujia, vaan niiden kopiota, joten itse muuttujien sisältö ei muutu funktioiden suorittamisen seurauksena.

/* Määrittele funktio, joka lisää 10 välitettyyn muuttujaan ja tulostaa tuloksen sivulle */ function plus(a)( a=a+10; document.write("Funktion tulos: " + a+"
"); ) var a=25; document.write("Muuttujan arvo ennen funktiokutsua: "+a+"
"); // Kutsu funktiota, joka välittää sen muuttujan plus(a); document.write("Muuttujan arvo funktion kutsun jälkeen: "+a+"
");

Katsaus

Jos haluat käyttää globaalia muuttujaa funktiosta sen kopion sijaan, käytä ikkuna.muuttujan_nimi.

Funktio plus(a)(ikkuna.a=a+10; ) var a=25; document.write("Muuttujan arvo ennen funktiokutsua: "+a+"
"); plus(a); document.write("Muuttujan arvo funktiokutsun jälkeen: "+a+"
");

Katsaus

paluu komento

Komennolla palata Voit palauttaa arvoja funktioista.



Katsaus

Sisäänrakennetut toiminnot

Käyttäjän määrittämien toimintojen lisäksi JavaScriptissä on myös sisäänrakennetut toiminnot.

Esimerkiksi sisäänrakennettu toiminto on rajallinen voit tarkistaa, onko välitetty arvo kelvollinen numero.

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90.33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("Tämä on merkkijono")+"
");

Katsaus

Huomautus: täydellinen lista sisäänrakennetut JavaScript-toiminnot, jotka löydät kohdasta .

Paikalliset ja globaalit muuttujat

Funktioiden sisällä luotuja muuttujia kutsutaan paikalliset muuttujat. Voit käyttää tällaisia ​​muuttujia vain niissä funktioissa, joissa ne on määritetty.

Toimintokoodin suorittamisen jälkeen tällaiset muuttujat tuhoutuvat. Tämä tarkoittaa, että samannimiset muuttujat voidaan määritellä eri funktioissa.

Toimintokoodin ulkopuolella luotuja muuttujia kutsutaan globaaleja muuttujia tällaisia ​​muuttujia voidaan käyttää mistä tahansa koodin kohdasta.

Jos määrität funktion sisällä muuttujan ilman varia, siitä tulee myös globaali.

Globaalit muuttujat tuhoutuvat vain, kun sivu suljetaan.



Katsaus

Huomautus: kun se näytetään, var2 on tyhjä, koska func1 toimii var2:n paikallisessa "versiossa".

Anonyymien toimintojen käyttäminen

Kutsutaan funktioita, jotka eivät sisällä nimeä ilmoitettuna anonyymi.

Nimettömiä funktioita ei periaatteessa ilmoiteta niiden myöhempää kutsua varten koodista tavallisiksi funktioiksi, vaan siirtymistä varten muille funktioille parametrina.

Funktio arrMap(arr,func)( var res=new Array; for (var i=0;i ");

Katsaus

Tee se itse

Harjoitus 1. Korjaa alla olevan koodin virheet:

Harjoitus 1

Korjaa koodissa oleva virhe.



Tehtävä 2.

  1. Toista funktioiden 1-3 koodi tutkimalla niiden käyttäytymistä eri parametreja välitettäessä.
  2. Määritä avainsana vuorovaikutuksessa funktion 4 kanssa.

Tehtävä 2

//Kutsu ensimmäinen salainen funktio document.write(secfunc1(4,12) + "
"); // Toisen salaisen funktion kutsuminen document.write(secfunc2(100,10) + "
"); //Kutsu kolmas salainen funktio secfunc3(23,10); document.write("
"); // Neljännen salaisen funktion kutsuminen secfunc4("n");

Ota JavaScript käyttöön käyttääksesi Disqus-kommentointijärjestelmää.




Ylös