Izjave o dovršetku Js funkcije. JavaScript funkcije. Funkcije i nuspojave

Naredbe skoka i rukovanje iznimkama

Druga kategorija JavaScript operatora su operatori skokova. Kao što ime sugerira, ovi operatori uzrokuju skok JavaScript tumača na drugo mjesto u programskom kodu. Naredba break uzrokuje da interpreter skoči na kraj petlje ili druge naredbe. Naredba continue uzrokuje da interpreter preskoči ostatak tijela petlje, skoči natrag na početak petlje i počne izvršavati novu iteraciju. JavaScript ima mogućnost označavanja izjava imenima, tako da izjave break i continue mogu eksplicitno naznačiti kojoj petlji ili drugoj izjavi pripadaju.

Naredba return uzrokuje da interpreter skoči s pozvane funkcije natrag na točku na kojoj je pozvana i vrati vrijednost poziva. Izjava throw pokreće iznimku i dizajnirana je da radi zajedno s izjavama try/catch/finally, koje definiraju blok koda za obradu iznimke. Ovo je prilično složena vrsta operatora skoka: kada se dogodi iznimka, tumač skače na najbliži okružujući rukovatelj iznimkama, koji može biti u istoj funkciji ili više u povratnom stogu pozvane funkcije.

Svi ovi operatori skokova detaljnije su opisani u sljedećim pododjeljcima.

Oznake s uputama

Svaka instrukcija može biti označena identifikatorom i dvotočkom:

ID: uputa

Označavanjem instrukcije dajete joj naziv koji se zatim može koristiti kao referenca bilo gdje u programu. Možete označiti bilo koju instrukciju, ali ima smisla označavati samo instrukcije koje imaju tijelo, kao što su petlje i uvjetne naredbe.

Dodjeljivanjem naziva petlji, ono se zatim može koristiti u naredbama break i continue, unutar petlje za izlazak iz nje ili za skok na početak petlje do sljedeće iteracije. Izjave break i continue jedine su JavaScript izjave koje mogu sadržavati oznake—o njima ćemo detaljnije govoriti kasnije. Slijedi primjer naredbe while s oznakom i naredbe continue koja koristi tu oznaku:

Glavna petlja: while (token != null) ( // Programski kod izostavljen... nastavak glavne petlje; // Prijelaz na sljedeću iteraciju imenovane petlje)

Identifikator koji se koristi kao oznaka izjave može biti bilo koji važeći JavaScript identifikator osim rezervirane riječi. Imena oznaka odvojena su od naziva varijabli i funkcija, tako da se identifikatori koji odgovaraju nazivima varijabli ili funkcija mogu koristiti kao oznake.

Oznake instrukcija definirane su samo unutar instrukcija na koje se odnose (i, naravno, unutar njihovih ugniježđenih instrukcija). Ugniježđene izjave ne mogu se označiti istim identifikatorima kao izjave koje ih sadrže, ali dvije neovisne izjave mogu biti označene istom oznakom. Označene upute mogu se ponovno označiti. To jest, svaka uputa može imati mnogo oznaka.

break izjava

Naredba break uzrokuje trenutni izlaz iz najunutarnje petlje ili naredbe switch. Prethodno smo vidjeli primjere korištenja naredbe break unutar naredbe switch. U petljama se obično koristi za trenutni izlaz iz petlje kada se petlja treba prekinuti iz nekog razloga.

Kada petlja ima vrlo složene uvjete završetka, često je lakše implementirati te uvjete korištenjem break naredbe nego pokušati ih izraziti u jednom uvjetnom izrazu petlje. Sljedeći primjer pokušava pronaći element polja s određenom vrijednošću. Petlja završava normalno kada dođe do kraja niza ili s break naredbom kada se pronađe vrijednost koja se traži:

Var arr = ["a","b","c","d","d"], rezultat; za (var i = 0; i

U JavaScriptu možete navesti naziv oznake nakon ključne riječi break (identifikator bez dvotočke):

prekinuti label_name;

Kada se naredba break koristi s oznakom, ona skače na kraj imenovane naredbe ili prekida njezino izvođenje. Ako ne postoji naredba s navedenom oznakom, pokušaj korištenja ovog oblika naredbe break generira sintaktičku pogrešku. Imenovani iskaz ne mora biti izraz petlje ili switch. Break naredba s oznakom može izaći iz bilo koje sadržavajuće naredbe. Instrukcija omotača može biti čak i jednostavan blok instrukcija u vitičastim zagradama s jedinom svrhom označavanja.

Ne možete umetnuti znak novog retka između ključne riječi break i naziva oznake. To je zato što JavaScript tumač automatski umeće točku-zarez koji nedostaje: ako podijelite redak koda između ključne riječi break i sljedeće oznake, tumač će pretpostaviti da ste mislili na jednostavan oblik iskaza bez oznake i dodati će točku-zarez.

Označena naredba break je potrebna samo kada želite prekinuti izvođenje naredbe koja nije najbliža petlja ili naredba switch.

Nastavi operator

Naredba continue slična je naredbi break. Međutim, umjesto izlaska iz petlje, naredba continue započinje novu iteraciju petlje. Sintaksa naredbe continue jednostavna je kao i sintaksa naredbe break. Naredba continue također se može koristiti s oznakom.

Naredba continue, u obliku bez oznake i s oznakom, može se koristiti samo u tijelu petlje. Korištenje bilo gdje drugdje dovodi do sintaktičke pogreške. Kada se izvrši naredba continue, trenutna iteracija petlje se prekida i započinje sljedeća. To znači različite stvari za različite vrste petlji:

    U while petlji, izraz naveden na početku petlje ponovno se provjerava, i ako je istinit, tijelo petlje se izvršava od početka.

    Do/while petlja skače na kraj petlje, gdje se uvjet ponovno provjerava prije ponovnog izvođenja petlje.

    Petlja for procjenjuje izraz inkrementa i ponovno procjenjuje testni izraz kako bi odredila treba li izvršiti sljedeću iteraciju.

    U for/in petlji, petlja počinje ispočetka dodjeljivanjem navedene varijable imenu sljedećeg svojstva.

Obratite pažnju na razlike u ponašanju naredbe continue u while i for petljama. Dok se petlja vraća izravno na svoj uvjet, dok for petlja prvo procjenjuje inkrementalni izraz, a zatim se vraća na uvjet. Sljedeći primjer pokazuje upotrebu naredbe continue bez oznake za izlaz iz trenutne iteracije petlje za parne brojeve:

Var zbroj = 0; // Izračunaj zbroj neparnih brojeva od 0 - 10 za (var i = 0; i

Naredba continue, kao i break, može se koristiti u ugniježđenim petljama u obliku koji uključuje oznaku, a zatim petlja koja se ponovno pokreće ne mora nužno biti petlja koja izravno sadrži naredbu continue. Osim toga, kao i kod prekida, prijelomi redaka nisu dopušteni između ključne riječi continue i naziva oznake.

povratna izjava

Poziv funkcije je izraz i, kao i svi izrazi, ima vrijednost. Naredba return unutar funkcija koristi se za određivanje vrijednosti koju vraća funkcija. Naredba return može se pojaviti samo u tijelu funkcije. Njegova prisutnost na bilo kojem drugom mjestu je sintaktička pogreška. Kada se izvrši naredba return, funkcija vraća vrijednost izraza programu koji poziva. Na primjer:

Ako funkcija nema povratnu naredbu, kada se pozove, tumač će izvršiti naredbe u tijelu funkcije jednu po jednu dok ne dođe do kraja funkcije, a zatim će vratiti kontrolu programu koji ju je pozvao. U tom će slučaju pozivni izraz vratiti nedefinirano. Naredba return često je posljednja naredba u funkciji, ali je potpuno neobvezna: funkcija će vratiti kontrolu pozivajućem programu čim se dosegne naredba return, čak i ako nakon nje slijede druge naredbe u tijelu funkcije.

Naredba return također se može koristiti bez izraza, u kojem slučaju jednostavno prekida funkciju i vraća se nedefinirano u program koji poziva. Na primjer:

Funkcija myFun(arr) ( // Ako niz sadrži negativne brojeve, prekinuti funkciju za (var i = 0; i

izjava bacanja

Iznimka je signal koji pokazuje da je došlo do neke izvanredne situacije ili pogreške. Bacanje iznimke je način signaliziranja takve pogreške ili iznimke. Uhvatiti izuzetak znači obraditi ga, tj. poduzeti radnje potrebne ili prikladne za oporavak od iznimke.

U JavaScriptu se iznimke izbacuju kada se dogodi pogreška u vremenu izvođenja i kada je program eksplicitno podigne koristeći naredbu throw. Iznimke se hvataju pomoću naredbi try/catch/finally, koje su kasnije opisane.

Izjava throw ima sljedeću sintaksu:

bacati izraz;

Rezultat izraza može biti vrijednost bilo kojeg tipa. Naredbi throw može se proslijediti broj koji predstavlja šifru pogreške ili niz koji sadrži tekst poruke o pogrešci. JavaScript interpreter izbacuje iznimke koristeći instancu klase Error jedne od njenih podklasa, a vi možete koristiti sličan pristup. Objekt Error ima svojstvo ime, koji definira vrstu pogreške i svojstvo poruka koji sadrži niz proslijeđen funkciji konstruktora. Slijedi primjer funkcije koja pokreće objekt Error kada se pozove s nevažećim argumentom:

// Funkcija broja faktorijel funkcija faktorijal(broj) ( // Ako ulazni argument nije valjana vrijednost, // izbacuje se iznimka! if (broj 1; i *= broj, broj--); /* prazno tijelo petlje */ return i ) console.log("5! = ", faktorijel(5)); console.log("-3! = ", faktorijel(-3));

Kada se pokrene iznimka, JavaScript interpreter odmah prekida normalno izvođenje programa i skače na najbliži rukovatelj iznimkama. Rukovatelji iznimkama koriste klauzulu try/catch/finally catch, koja je opisana u sljedećem odjeljku.

Ako blok koda u kojem se dogodila iznimka nema odgovarajuću catch konstrukciju, tumač ispituje sljedeći vanjski blok koda i provjerava je li s njim povezan rukovatelj iznimkom. Ovo se nastavlja dok se ne pronađe rukovatelj.

Ako je iznimka izbačena u funkciji koja ne sadrži konstrukciju try/catch/finally za obradu, iznimka se širi do koda koji je pozvao funkciju. Na ovaj način, iznimke se šire kroz leksičku strukturu JavaScript metoda prema gore prema stogu poziva. Ako rukovatelj iznimkama nikada nije pronađen, iznimka se tretira kao pogreška i prijavljuje se korisniku.

pokušati/uhvatiti/konačno konstruirati

Konstrukcija try/catch/finally implementira mehanizam rukovanja iznimkama u JavaScriptu. Naredba try u ovoj konstrukciji jednostavno definira blok koda u kojem se rukuje iznimkama. Nakon bloka try slijedi catch naredba s blokom naredbi koje se pozivaju ako se bilo gdje u bloku try pojavi iznimka. Nakon naredbe catch slijedi blok finally, koji sadrži konačni kod koji će se zajamčeno pokrenuti bez obzira na to što se događa u bloku try.

I blok catch i finally blok su izborni, ali barem jedan od njih mora biti prisutan nakon bloka try. Pokušajte, uhvatite i konačno blokovi počinju i završavaju vitičastim zagradama. Ovo je obavezan dio sintakse i ne može se izostaviti, čak i ako postoji samo jedna instrukcija između.

Sljedeći isječak ilustrira sintaksu i svrhu konstrukcije try/catch/finally:

Pokušajte ( // Ovaj kod obično radi glatko od početka do kraja. // Ali u nekom trenutku može izbaciti iznimku // bilo izravno korištenjem naredbe throw, ili neizravno // pozivanjem metode koja izbacuje iznimku. ) catch ( ex) ( // Naredbe u ovom bloku se izvršavaju ako i samo ako se // dogodi iznimka u bloku pokušaja. Ove naredbe mogu koristiti lokalnu varijablu ex, koja se // odnosi na Error objekt ili drugu vrijednost navedenu u bacanju naredba. // Ovaj blok može ili obraditi iznimku na neki način, // ignorirati je dok radi nešto drugo, ili ponovno izbaciti iznimku koristeći naredbu throw) finally ( // Ovaj blok sadrži naredbe koje se uvijek izvršavaju bez obzira na to. , // što se dogodilo u bloku pokušaja Izvršavaju se ako je blok pokušaja dovršen: // 1) kao i obično, dostizanjem kraja bloka // 2) zbog naredbe break, continue ili return // 3) s naredbom break. iznimka obrađena u bloku catch iznad // ​​4) s neuhvaćenom iznimkom koja se nastavlja širiti // na više razine)

Imajte na umu da ključnu riječ catch prati identifikator u zagradama. Ovaj je identifikator sličan parametru funkcije. Kada je iznimka uhvaćena, ovaj će se parametar dodijeliti iznimci (na primjer, Error objekt). Za razliku od obične varijable, identifikator povezan s catch naredbom postoji samo u tijelu catch bloka.

Sljedeći je realističniji primjer konstrukcije pokušaj/uhvati. Poziva metodu factorial() definiranu u prethodnom primjeru i klijentske JavaScript metode prompt() i alert() za organiziranje ulaza i izlaza:

Pokušajte ( // Tražite od korisnika broj var n = Number(prompt("Enter a positive number", "")); // Izračunajte faktorijel broja, uz pretpostavku // da su ulazni podaci točni var f = faktorijel(n); // Ispis rezultata alert(n + "! = " + f); catch (ex) ( // Ako su podaci netočni, kontrola će se prenijeti ovdje alert(ex); // Obavijestiti korisnika o grešci)

Ako korisnik unese negativan broj, pojavljuje se poruka upozorenja:

Ovo je primjer konstrukcije try/catch bez finally klauzule. Iako se finally ne koristi tako često kao catch, ipak je ponekad koristan. Zajamčeno je da će se finally blok izvršiti ako je barem neki dio try bloka izvršen, bez obzira na to kako je kod u try bloku završio. Ova se značajka obično koristi za izvođenje završnih operacija nakon izvođenja koda u nastavku pokušaja.

U normalnoj situaciji, kontrola dolazi do kraja try bloka i zatim se pomiče do finally bloka, koji izvodi potrebne završne operacije. Ako kontrola izađe iz bloka try kao rezultat naredbe return, continue ili break, finally blok se izvršava prije nego se kontrola prenese negdje drugdje.

Ako se iznimka dogodi u bloku try i postoji odgovarajući blok catch koji to rješava, kontrola prvo prelazi na blok catch, a zatim na blok finally. Ako ne postoji lokalni catch blok, kontrola prvo prelazi na finally blok, a zatim se premješta na najbliži vanjski catch blok koji može obraditi iznimku.

Ako sam finally blok prenosi kontrolu korištenjem naredbe return, continue, break ili throw ili pozivanjem metode koja izbacuje iznimku, naredba prijenosa na čekanju se poništava i izvršava se nova. Na primjer, ako finally blok izbaci iznimku, ta će iznimka zamijeniti sve prethodno izbačene iznimke.

Ljudi misle da je informatika umjetnost za genije. U stvarnosti je obrnuto - samo mnogo ljudi izrađuje stvari koje stoje jedna na drugoj, kao da čine zid od malih kamenčića.

Donald Knuth

Već ste vidjeli pozive funkcija poput alert. Funkcije su kruh i maslac JavaScript programiranja. Ideja omotanja dijela programa i pozivanja kao varijable vrlo je popularna. To je alat za strukturiranje velikih programa, smanjenje ponavljanja, imenovanje potprograma i međusobno izoliranje potprograma.

Najočitija upotreba funkcija je stvaranje novog rječnika. Izmišljanje riječi za običnu ljudsku prozu je loš oblik. Ovo je neophodno u programskom jeziku.

Prosječna odrasla osoba koja govori ruski zna otprilike 10.000 riječi. Rijedak programski jezik sadrži 10 000 ugrađenih naredbi. I vokabular programskog jezika je jasnije definiran, pa je manje fleksibilan od ljudskog. Stoga mu obično moramo dodati vlastite riječi kako bismo izbjegli nepotrebno ponavljanje.

Definicija funkcije Definicija funkcije je normalna definicija varijable, gdje je vrijednost koju varijabla prima funkcija. Na primjer, sljedeći kod definira kvadrat varijable, koji se odnosi na funkciju koja izračunava kvadrat zadanog broja:

Var square = function(x) ( return x * x; ); konzola.log(kvadrat(12)); // → 144

Funkcija se stvara izrazom koji počinje ključnom riječi function. Funkcije imaju skup parametara (u ovom slučaju samo x) i tijelo koje sadrži upute koje se moraju izvršiti kada se funkcija pozove. Tijelo funkcije uvijek je u vitičastim zagradama, čak i ako se sastoji od jedne izjave.

Funkcija može imati nekoliko parametara ili niti jedan. U sljedećem primjeru makeNoise nema popis parametara, ali power ima dva:

Var makeNoise = function() ( console.log("Sranje!"); ); napraviti buku(); // → Khrya! var power = funkcija (baza, eksponent) ( var rezultat = 1; for (var count = 0; count< exponent; count++) result *= base; return result; }; console.log(power(2, 10)); // → 1024

Neke funkcije vraćaju vrijednost, poput snage i kvadrata, druge ne, poput makeNoisea, što proizvodi samo nuspojavu. Naredba return navodi vrijednost koju vraća funkcija. Kada obrada programa dođe do ove instrukcije, odmah izlazi iz funkcije i vraća ovu vrijednost na mjesto u kodu s kojeg je funkcija pozvana. povratak bez izraza vraća nedefinirano.

Parametri i opseg Funkcijski parametri su iste varijable, ali njihove početne vrijednosti su postavljene kada se funkcija poziva, a ne u njenom kodu.

Važno svojstvo funkcija je da su varijable stvorene unutar funkcije (uključujući parametre) lokalne za tu funkciju. To znači da će se u primjeru snage varijabla rezultata kreirati svaki put kad se funkcija pozove, a te njezine pojedinačne inkarnacije neće imati nikakve veze jedna s drugom.

Ovaj lokalitet varijable primjenjuje se samo na parametre i varijable stvorene unutar funkcija. Varijable definirane izvan bilo koje funkcije zovu se globalne jer su vidljive u cijelom programu. Također možete pristupiti takvim varijablama unutar funkcije, osim ako ne deklarirate lokalnu varijablu s istim imenom.

Sljedeći kod to ilustrira. Definira i poziva dvije funkcije koje dodjeljuju vrijednost varijabli x. Prvi ga deklarira kao lokalni, mijenjajući tako samo lokalnu varijablu. Drugi ne deklarira, pa se rad s x unutar funkcije odnosi na globalnu varijablu x definiranu na početku primjera.

Var x = "izvan"; var f1 = function() ( var x = "unutar f1"; ); f1(); konzola.log(x); // → izvan var f2 = funkcija() ( x = "unutar f2"; ); f2(); konzola.log(x); // → unutar f2

Ovo ponašanje pomaže u sprječavanju slučajnih interakcija između funkcija. Kad bi se sve varijable koristile bilo gdje u programu, bilo bi vrlo teško osigurati da se jedna varijabla ne koristi u različite svrhe. A ako biste ponovno upotrijebili varijablu, naišli biste na čudne učinke kada kod treće strane pokvari vrijednosti vaše varijable. Tretirajući lokalne varijable funkcije tako da postoje samo unutar funkcije, jezik omogućuje rad s funkcijama kao da su odvojeni mali svemiri, što vam omogućuje da ne brinete o cijelom kodu.

Ugniježđeni opseg JavaScript razlikuje više od samo globalnih i lokalnih varijabli. Funkcije se mogu definirati unutar funkcija, što rezultira višestrukim razinama lokalnosti.

Na primjer, sljedeća prilično besmislena funkcija sadrži još dvije unutra:

Var landscape = function() ( var result = ""; var flat = function(size) ( for (var 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()); // → ___/""""\______/"\_

Ravne i planinske funkcije vide varijablu rezultata jer se nalaze unutar funkcije koja ju definira. Ali ne mogu vidjeti međusobne varijable brojanja jer su varijable jedne funkcije izvan opsega druge. A okruženje izvan pejzažne funkcije ne vidi niti jednu od varijabli definiranih unutar ove funkcije.

Ukratko, unutar svakog lokalnog opsega možete vidjeti sve opsege koji ga sadrže. Skup varijabli dostupnih unutar funkcije određen je mjestom na kojem je funkcija deklarirana u programu. Sve varijable iz blokova koji okružuju definiciju funkcije su vidljive - uključujući one definirane na najvišoj razini u glavnom programu. Ovaj pristup opsegu naziva se leksički.

Ljudi koji su proučavali druge programske jezike mogu pomisliti da svaki blok u vitičastim zagradama stvara vlastito lokalno okruženje. Ali u JavaScriptu samo funkcije stvaraju opseg. Možete koristiti samostojeće blokove:

Var nešto = 1; ( var something = 2; // Učini nešto s varijablom something... ) // Izašao iz bloka...

Ali nešto unutar bloka je ista varijabla kao i izvan njega. Iako su takvi blokovi dopušteni, ima smisla koristiti ih samo za if naredbe i petlje.

Ako vam se ovo čini čudnim, niste jedini koji tako misli. JavaScript 1.7 uveo je ključnu riječ let, koja radi kao var, ali stvara varijable koje su lokalne za bilo koji blok, a ne samo za funkciju.

Funkcije kao vrijednosti Imena funkcija obično se koriste kao nazivi za dio programa. Takva se varijabla postavlja jednom i ne mijenja se. Stoga je lako pobrkati funkciju i njezino ime.

Ali to su dvije različite stvari. Poziv funkcije može se koristiti kao jednostavna varijabla - na primjer, koristiti u bilo kojem izrazu. Moguće je pohraniti poziv funkcije u novu varijablu, proslijediti je kao parametar drugoj funkciji i tako dalje. Također, varijabla koja pohranjuje poziv funkcije ostaje regularna varijabla i njena se vrijednost može promijeniti:

Var launchMissiles = funkcija(vrijednost) ( ​​missileSystem.launch("ili!");); if (safeMode) launchMissiles = funkcija(vrijednost) (/* odustani */);

U 5. poglavlju raspravljat ćemo o prekrasnim stvarima koje možete učiniti prosljeđivanjem poziva funkcija drugim funkcijama.

Deklariranje funkcija Postoji kraća verzija izraza “var square = function...”. Ključna riječ funkcija može se koristiti na početku izjave:

Funkcija square(x) ( return x * x; )

Ovo je deklaracija funkcije. Izjava definira kvadratnu varijablu i dodjeljuje joj zadanu funkciju. Zasada je dobro. U takvoj definiciji postoji samo jedna zamka.

Console.log("Budućnost kaže:", budućnost()); function future() ( return "JOŠ uvijek nemamo leteće automobile."; )

Ovaj kod radi iako je funkcija deklarirana ispod koda koji je koristi. To je zato što deklaracije funkcija nisu dio normalnog izvršavanja programa odozgo prema dolje. Oni su "premješteni" na vrh svog opsega i mogu se pozvati bilo kojim kodom u tom opsegu. Ponekad je to zgodno jer možete pisati kod redoslijedom koji ima najviše smisla, a da ne morate brinuti o definiranju svih funkcija iznad gdje se koriste.

Što se događa ako deklaraciju funkcije stavimo unutar uvjetnog bloka ili petlje? Ne morate to učiniti. Povijesno gledano, različite platforme koje izvode JavaScript drugačije su rješavale takve slučajeve, a trenutni jezični standard to zabranjuje. Ako želite da se vaši programi izvode sekvencijalno, koristite deklaracije funkcija samo unutar drugih funkcija ili glavnog programa.

Primjer funkcije() ( funkcija a() () // Normalno ako (nešto) ( funkcija b() () // Ay-yay-yay! ) )

Stog poziva Korisno je pobliže pogledati kako redoslijed izvršenja funkcionira s funkcijama. Evo jednostavnog programa s nekoliko poziva funkcija:

Funkcija greet(who) ( console.log("Zdravo, " + tko); ) greet("Semyon"); console.log("Pokeda");

Obrađuje se otprilike ovako: pozivanje greet uzrokuje skok prolaza na početak funkcije. Poziva ugrađenu funkciju console.log, koja presreće kontrolu, radi svoje i vraća kontrolu. Zatim dođe do kraja pozdrava, pa se vrati na mjesto odakle je pozvan. Sljedeći red ponovno poziva console.log.

To se shematski može prikazati ovako:

Top greet console.log greet top console.log top

Budući da se funkcija mora vratiti na mjesto s kojeg je pozvana, računalo mora zapamtiti kontekst iz kojeg je funkcija pozvana. U jednom slučaju, console.log bi se trebao vratiti na pozdrav. U drugom se vraća na kraj programa.

Mjesto gdje računalo pamti kontekst naziva se stog. Svaki put kada se pozove funkcija, trenutni kontekst se gura na vrh stoga. Kada se funkcija vrati, izbacuje gornji kontekst iz stoga i koristi ga za nastavak rada.

Za pohranjivanje hrpe potreban je memorijski prostor. Kad stog postane prevelik, računalo će prestati s izvršavanjem i reći nešto poput "prelijevanje stoga" ili "previše rekurzije". Sljedeći kod to pokazuje - on računalu postavlja vrlo složeno pitanje, što dovodi do beskrajnih skokova između dvije funkcije. Točnije, radilo bi se o beskonačnim skokovima kada bi računalo imalo beskonačni stog. U stvarnosti se stog prelijeva.

Funkcija chicken() ( return egg(); ) function egg() ( return chicken(); ) console.log(chicken() + " je došao prvi."); // → ??

Neobavezni argumenti Sljedeći kod potpuno je legalan i radi bez problema:

Alert("Zdravo", "Dobro veče", "Zdravo svima!");

Službeno, funkcija uzima jedan argument. No, kad je suočena s ovakvim izazovom, ne žali se. Ona ignorira ostale argumente i pokazuje "Zdravo."

JavaScript je vrlo poseban u pogledu broja argumenata proslijeđenih funkciji. Ako prenesete previše, dodatni će biti zanemareni. Premalo i onima koji nedostaju bit će dodijeljena nedefinirana vrijednost.

Loša strana ovog pristupa je da je moguće - pa čak i vjerojatno - proslijediti pogrešan broj argumenata funkciji, a da se nitko ne žali na to.

Prednost je u tome što možete kreirati funkcije koje uzimaju neobavezne argumente. Na primjer, u sljedećoj verziji funkcije potencije, ona se može pozvati s dva ili jednim argumentom - u potonjem slučaju, eksponent će biti jednak dva, a funkcija radi kao kvadrat.

Funkcijska snaga (baza, eksponent) ( if (eksponent == nedefinirano) eksponent = 2; prom. rezultat = 1; for (var zbroj = 0; broj< exponent; count++) result *= base; return result; } console.log(power(4)); // → 16 console.log(power(4, 3)); // → 64

U sljedećem poglavlju ćemo vidjeti kako u tijelu funkcije možete saznati točan broj argumenata koji su joj proslijeđeni. Ovo je korisno jer... omogućuje stvaranje funkcije koja uzima bilo koji broj argumenata. Na primjer, console.log koristi ovo svojstvo i ispisuje sve argumente koji su mu proslijeđeni:

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

Zatvaranja Mogućnost korištenja poziva funkcija kao varijabli, zajedno s činjenicom da se lokalne varijable stvaraju iznova svaki put kada se funkcija pozove, dovodi nas do zanimljivog pitanja. Što se događa s lokalnim varijablama kada funkcija prestane raditi?

Sljedeći primjer ilustrira ovaj problem. Deklariše funkciju wrapValue, koja stvara lokalnu varijablu. Zatim vraća funkciju koja čita ovu lokalnu varijablu i vraća njezinu vrijednost.

Funkcija wrapValue(n) ( var lokalna varijabla = n; return funkcija() ( return lokalna varijabla; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); console.log(wrap1()); // → 1 konzola.log(wrap2()); // → 2

Ovo je važeće i radi kako treba - pristup varijabli ostaje. Štoviše, višestruke instance iste varijable mogu postojati u isto vrijeme, što dodatno potvrđuje činjenicu da se lokalne varijable ponovno kreiraju sa svakim pozivom funkcije.

Ova mogućnost rada s referencom na instancu lokalne varijable naziva se zatvaranje. Funkcija koja zatvara lokalne varijable naziva se zatvaranje. Ne samo da vas oslobađa brige o promjenjivim vijekovima trajanja, već vam također omogućuje kreativno korištenje funkcija.

Uz male izmjene, pretvaramo naš primjer u funkciju koja množi brojeve bilo kojim danim brojem.

Funkcija množitelj(faktor) ( vrati funkciju(broj) ( vrati broj * faktor; ); ) var two = multiplier(2); konzola.log(dvaput(5)); // → 10

Zasebna varijabla poput localVariable iz primjera wrapValue više nije potrebna. Budući da je parametar sam po sebi lokalna varijabla.

Trebat će praksa da počnete razmišljati na ovaj način. Dobar mentalni model je zamisliti da funkcija zamrzne kod u svom tijelu i zamota ga u paket. Kada vidite return function(...) (...), zamislite to kao upravljačku ploču za dio koda zamrznut za kasniju upotrebu.

U našem primjeru multiplikator vraća zamrznuti dio koda koji spremamo u varijablu two. Zadnji redak poziva funkciju sadržanu u varijabli, koja uzrokuje aktiviranje pohranjenog koda (povratni broj * faktor;). Još uvijek ima pristup varijabli faktora koja je definirana prilikom pozivanja množitelja, a također ima pristup argumentu proslijeđenom tijekom odmrzavanja (5) kao numeričkom parametru.

Rekurzija Funkcija može pozvati samu sebe sve dok pazi da ne prelije stog. Ova funkcija se zove rekurzivna. Evo primjera alternativne implementacije potenciranja:

Funkcija power(baza, eksponent) ( if (eksponent == 0) vrati 1; inače vrati bazu * power(baza, eksponent - 1); ) console.log(power(2, 3)); // → 8

Ovo je otprilike način na koji matematičari definiraju stepenovanje, a možda ovo opisuje koncept elegantnije od ciklusa. Funkcija sama sebe poziva mnogo puta s različitim argumentima kako bi postigla višestruko množenje.

Međutim, ova implementacija ima problem - u običnom JavaScript okruženju je 10 puta sporija od verzije s petljom. Hodanje kroz petlju je jeftinije od pozivanja funkcije.

Dilema brzina nasuprot eleganciji vrlo je zanimljiva. Postoji određeni jaz između pogodnosti za ljude i pogodnosti za strojeve. Svaki se program može ubrzati tako da bude veći i zamršeniji. Programer mora pronaći odgovarajuću ravnotežu.

U slučaju prvog potenciranja, neelegantna petlja je prilično jednostavna i jasna. Nema smisla zamijeniti ga rekurzijom. Često se, međutim, programi bave tako složenim konceptima da se želi smanjiti učinkovitost povećanjem čitljivosti.

Osnovno pravilo, koje je već više puta ponovljeno, a s kojim se u potpunosti slažem, jest da ne brinete o performansama dok niste sasvim sigurni da program usporava. Ako je tako, pronađite dijelove koji traju najduže i tamo zamijenite eleganciju za učinkovitost.

Naravno, ne bismo trebali odmah potpuno zanemariti performanse. U mnogim slučajevima, kao kod potenciranja, ne dobivamo puno jednostavnosti iz elegantnih rješenja. Ponekad iskusni programer može odmah vidjeti da jednostavan pristup nikada neće biti dovoljno brz.

Ovo ističem jer je previše programera početnika opsjednuto učinkovitošću čak i u malim stvarima. Rezultat je veći, složeniji i često nije bez pogrešaka. Takvi programi se pišu dulje, ali često ne rade mnogo brže.

Ali rekurzija nije uvijek samo manje učinkovita alternativa petljama. Neke probleme lakše je riješiti rekurzijom. Najčešće je to obilazak nekoliko grana stabla, od kojih se svaka može granati.

Evo zagonetke: možete dobiti beskonačan broj brojeva tako da počnete s brojem 1, a zatim ili dodate 5 ili pomnožite s 3. Kako napisati funkciju koja, zadani broj, pokušava pronaći niz zbrajanja i množenja koji vode do određenog broja? Na primjer, broj 13 može se dobiti tako da se prvo pomnoži 1 sa 3, a zatim se dva puta doda 5. A broj 15 se uopće ne može dobiti na ovaj način.

Rekurzivno rješenje:

Funkcija findSolution(target) ( funkcija find(start, history) ( if (start == target) vrati povijest; else if (start > target) return null; else return find(start + 5, "(" + history + " + 5)") || find(start * 3, "(" + history + " * 3)"); ) return find(1, "1"); ) console.log(findSolution(24)); // → (((1 * 3) + 5) * 3)

Ovaj primjer ne mora nužno pronaći najkraće rješenje - zadovoljava ga bilo tko. Ne očekujem da odmah shvatite kako program radi. Ali hajdemo razumjeti ovu sjajnu vježbu rekurzivnog razmišljanja.

Unutarnja funkcija find radi rekurziju. Potrebna su dva argumenta - trenutni broj i niz koji sadrži zapis o tome kako smo došli do tog broja. I vraća niz koji prikazuje naš niz koraka ili null.

Da bi to učinila, funkcija izvodi jednu od tri radnje. Ako je zadani broj jednak cilju, onda je trenutna priča upravo način da se to postigne, pa se vraća. Ako je zadani broj veći od cilja, nema smisla dalje množiti i zbrajati jer će se samo povećavati. A ako još nismo dosegli cilj, funkcija isprobava obje moguće staze počevši od zadanog broja. Saziva se dvaput, jednom sa svakom metodom. Ako prvi poziv ne vrati null, vraća se. U drugom slučaju vraća se drugi.

Da bismo bolje razumjeli kako funkcija postiže željeni učinak, pogledajmo pozive koje upućuje kako bi pronašla rješenje za broj 13.

Nađi(1, "1") nađi(6, "(1 + 5)") nađi(11, "((1 + 5) + 5)") nađi(16, "(((1 + 5) + 5 ) + 5)") prevelik nalaz(33, "(((1 + 5) + 5) * 3)") prevelik nalaz(18, "((1 + 5) * 3)") prevelik nalaz( 3, "(1 * 3)") pronađi(8, "((1 * 3) + 5)") nađi(13, "(((1 * 3) + 5) + 5)") pronađeno!

Udubljenje pokazuje dubinu stoga poziva. Prvi put funkcija pronalaženja sama sebe poziva dvaput da provjeri rješenja koja počinju s (1 + 5) i (1 * 3). Prvi poziv traži rješenje koje počinje s (1 + 5) i koristi rekurziju za provjeru svih rješenja koja daju broj manji ili jednak traženom broju. Ne pronalazi ga i vraća null. Zatim operator || i prelazi na poziv funkcije koja ispituje opciju (1 * 3). Ovdje imamo sreće jer u trećem rekurzivnom pozivu dobivamo 13. Ovaj poziv vraća niz, a svaki od || usput prolazi ovu liniju više, što rezultira vraćanjem rješenja.

Rastuće funkcije Postoje dva više ili manje prirodna načina za uvođenje funkcija u program.

Prvi je da napišete sličan kod nekoliko puta. To treba izbjegavati - više koda znači više prostora za pogreške i više materijala za čitanje za one koji pokušavaju razumjeti program. Stoga uzimamo funkciju koja se ponavlja, dajemo joj dobro ime i stavljamo je u funkciju.

Drugi način je da otkrijete potrebu za nekom novom funkcionalnošću koja je vrijedna stavljanja u zasebnu funkciju. Počinjete s nazivom funkcije, a zatim pišete njezino tijelo. Možete čak početi pisanjem koda koji koristi funkciju prije nego što je sama funkcija definirana.

Koliko vam je teško imenovati funkciju pokazuje koliko dobro razumijete njezinu funkcionalnost. Uzmimo primjer. Moramo napisati program koji ispisuje dva broja, broj krava i kokoši na farmi, nakon čega slijede riječi "krave" i "kokoši". Brojevima ispred morate dodati nule tako da svaki zauzme točno tri mjesta.

007 Krave 011 Pilići

Očito, trebamo funkciju s dva argumenta. Počnimo s kodiranjem.
// print FarmInventory function printFarmInventory(cows, chickens) ( var cowString = String(cows); 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);

Ako nizu dodamo .length, dobit ćemo njegovu duljinu. Ispada da petlje while dodaju vodeće nule brojevima dok ne dobiju redak od 3 znaka.

Spreman! Ali baš kad smo htjeli poslati šifru farmeru (naravno, uz pozamašan ček), on nas zove i kaže da ima svinje na farmi i možemo li dodati prikaz broja svinja u program?

Moguće je, naravno. Ali kada počnemo kopirati i lijepiti kod iz ova četiri retka, shvatimo da moramo stati i razmisliti. Mora postojati bolji način. Trudimo se poboljšati program:

// izlaz S dodavanjem nula i oznaka funkcija printZeroPaddedWithLabel(number, label) ( var numberString = String(number); 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);

Djela! Ali naziv printZeroPaddedWithLabel pomalo je čudan. Kombinira tri stvari — izlaz, dodavanje nula i oznaku — u jednu funkciju. Umjesto umetanja cijelog fragmenta koji se ponavlja u funkciju, istaknimo jedan koncept:

// dodaj Zero funkciju zeroPad(number, width) ( var string = String(number); while (string.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);

Funkcija lijepog, jasnog naziva zeroPad olakšava razumijevanje koda. I može se koristiti u mnogim situacijama, ne samo u našem slučaju. Na primjer, za prikaz formatiranih tablica s brojevima.

Koliko bi značajke trebale biti pametne i svestrane? Možemo napisati jednostavnu funkciju koja dopunjuje broj nulama do tri mjesta ili sofisticiranu funkciju opće namjene za formatiranje brojeva koja podržava razlomke, negativne brojeve, poravnanje točaka, ispune itd.

Dobro je pravilo dodavati samo one funkcije za koje znate da će biti korisne. Ponekad je primamljivo stvoriti okvire opće namjene za svaku malu potrebu. Oduprite mu se. Nikada nećete završiti posao, samo ćete završiti tako što ćete napisati hrpu koda koji nitko neće koristiti.

Funkcije i nuspojave Funkcije se mogu grubo podijeliti na one koje su pozvane zbog svojih nuspojava i one koje su pozvane da dobiju neku vrijednost. Naravno, također je moguće kombinirati ova svojstva u jednoj funkciji.

Prva pomoćna funkcija u primjeru farme, printZeroPaddedWithLabel, pozvana je jer ima nuspojavu: ispisuje niz. Drugi, zeroPad, zbog povratne vrijednosti. I nije slučajno da je druga funkcija korisna češće od prve. Funkcije koje vraćaju vrijednosti lakše je međusobno kombinirati nego funkcije koje proizvode nuspojave.

Čista funkcija je posebna vrsta funkcije koja vraća vrijednost koja ne samo da nema nuspojava, nego također ne ovisi o nuspojavama ostatka koda - na primjer, ne radi s globalnim varijablama koje bi se mogle slučajno promijenio negdje drugdje. Čista funkcija, kada se pozove s istim argumentima, vraća isti rezultat (i ne radi ništa drugo) - što je prilično lijepo. S njom je lako raditi. Poziv takve funkcije može se mentalno zamijeniti rezultatom njezina rada, bez promjene značenja koda. Kada želite testirati takvu funkciju, možete je jednostavno pozvati i biti sigurni da ako radi u danom kontekstu, radit će u bilo kojem kontekstu. Manje čiste funkcije mogu vratiti različite rezultate ovisno o mnogim čimbenicima i imati nuspojave koje je teško testirati i objasniti.

Međutim, ne bi vam trebalo biti neugodno pisati funkcije koje nisu posve čiste ili započeti sveto čišćenje koda takvih funkcija. Nuspojave su često korisne. Ne postoji način da se napiše čista verzija funkcije console.log, a ova je funkcija vrlo korisna. Neke operacije je lakše izraziti pomoću nuspojava.

Sažetak Ovo vam je poglavlje pokazalo kako napisati vlastite funkcije. Kada se ključna riječ funkcije koristi kao izraz, vraća pokazivač na poziv funkcije. Kada se koristi kao instrukcija, možete deklarirati varijablu tako da joj dodijelite poziv funkcije.

Ključ za razumijevanje funkcija je lokalni opseg. Parametri i varijable deklarirani unutar funkcije su lokalni za nju, ponovno se stvaraju svaki put kada se pozove i nisu vidljivi izvana. Funkcije deklarirane unutar druge funkcije imaju pristup njezinom opsegu.

Vrlo je korisno razdvojiti različite zadatke koje obavlja program u funkcije. Ne morate se ponavljati; funkcije čine kod čitljivijim dijeleći ga na smislene dijelove, baš kao što poglavlja i odjeljci knjige pomažu organizirati uobičajeni tekst.

VježbeMinimum U prethodnom poglavlju spomenuli smo funkciju Math.min koja vraća najmanji od svojih argumenata. Sada možemo sami napisati takvu funkciju. Napišite funkciju min koja uzima dva argumenta i vraća najmanji od njih.

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

Rekurzija Vidjeli smo da se operator % (modulo) može koristiti za određivanje je li broj (%2) paran. Evo još jednog načina da to definirate:

Nula je parna.
Jedinica je neparna.
Bilo koji broj N ima isti paritet kao N-2.

Napišite rekurzivnu funkciju isEven prema ovim pravilima. Mora prihvatiti broj i vratiti Booleovu vrijednost.

Testirajte ga na 50 i 75. Pokušajte dati -1. Zašto se tako ponaša? Je li moguće to nekako popraviti?

Testirajte ga na 50 i 75. Pogledajte kako se ponaša na -1. Zašto? Možete li smisliti način da to popravite?

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

Brojanje graha.

Broj znakova N niza može se dobiti dodavanjem .charAt(N) (“string”.charAt(5)) tome - na sličan način kao što se duljina niza dobiva pomoću .length. Povratna vrijednost bit će niz koji se sastoji od jednog znaka (na primjer, "k"). Prvi znak niza ima poziciju 0, što znači da će zadnji znak imati poziciju string.length - 1. Drugim riječima, niz od dva znaka ima duljinu 2, a njegove pozicije znakova bit će 0 i 1.

Napišite funkciju countBs koja uzima niz kao argument i vraća broj znakova "B" sadržanih u nizu.

Zatim napišite funkciju pod nazivom countChar, koja radi nešto poput countBs, ali uzima drugi parametar - znak koji ćemo tražiti u nizu (umjesto da samo brojimo broj znakova "B"). Da biste to učinili, preradite funkciju countBs.

Funkcije su jedan od najvažnijih sastavnih dijelova koda u JavaScriptu.

Funkcije se sastoje od skupa naredbi i obično izvode jedan određeni zadatak (na primjer, zbrajanje brojeva, izračunavanje korijena itd.).

Kod postavljen u funkciju izvršit će se samo nakon eksplicitnog poziva ove funkcije.

Deklaracija funkcije

1. Sintaksa:

//Deklaracija funkcije functionFunctionname(ln1, ln2)( Funkcijski kod) //Pozivanje funkcijeFunctionname(ln1,lr2);

2. Sintaksa:

//Deklaracija funkcije var function name=function(ln1, ln2)(Function code) //Pozivanje funkcije function name(ln1,lr2);

naziv funkcije navodi naziv funkcije. Svaka funkcija na stranici mora imati jedinstveno ime. Naziv funkcije mora biti naveden latiničnim slovima i ne smije započeti brojevima.

ln1 i ln2 su varijable ili vrijednosti koje se mogu proslijediti unutar funkcije. Neograničen broj varijabli može se proslijediti svakoj funkciji.

Imajte na umu: čak i ako nijedna varijabla nije proslijeđena funkciji, ne zaboravite umetnuti zagrade "()" iza naziva funkcije.

Imajte na umu da nazivi funkcija u JavaScriptu razlikuju velika i mala slova.

Primjer JavaScript funkcije

Funkcija messageWrite() u donjem primjeru bit će izvršena tek nakon što se klikne gumb.

Imajte na umu da ovaj primjer koristi događaj onclick. JavaScript događaji će biti detaljno obrađeni kasnije u ovom vodiču.

// Funkcija piše tekst na stranicu function messageWrite() (document.write("Ovaj tekst je napisan na stranici pomoću JavaScript-a!"); )

Prijenos varijabli u funkcije

Funkcijama možete proslijediti neograničen broj varijabli.

Imajte na umu: sve manipulacije s varijablama unutar funkcija zapravo se ne izvode na samim varijablama, već na njihovoj kopiji, tako da se sadržaj samih varijabli ne mijenja kao rezultat izvršavanja funkcija.

/* Definirajmo funkciju koja dodaje 10 proslijeđenoj varijabli i prikazuje rezultat na stranici */ function plus(a)( a=a+10; document.write("Izlaz funkcije: " + a+"
"); ) var a=25; document.write("Vrijednost varijable prije poziva funkcije: "+a+"
"); // Pozovite funkciju prosljeđujući joj varijablu a plus(a); document.write("Vrijednost varijable nakon poziva funkcije: "+a+"
");

Brzi pregled

Za pristup globalnoj varijabli iz funkcije umjesto njezine kopije, koristite window.variable_name.

Funkcija plus(a)( window.a=a+10; ) var a=25; document.write("Vrijednost varijable prije poziva funkcije: "+a+"
"); plus(a); document.write("Vrijednost varijable nakon poziva funkcije: "+a+"
");

Brzi pregled

povratna naredba

Naredbom return možete vratiti vrijednosti iz funkcija.

//Funkcija sum vraća zbroj varijabli koje su joj proslijeđene function sum(v1,v2)( return v1+v2; ) document.write("5+6=" + sum(5,6) + "
"); document.write("10+4=" + sum(10,4) + "
");

Brzi pregled

Ugrađene funkcije

Osim korisnički definiranih funkcija, JavaScript ima i ugrađene funkcije.

Na primjer, ugrađena funkcija isFinite omogućuje vam da provjerite je li proslijeđena vrijednost važeći broj.

Document.write(isFinite(40)+"
"); document.write(isFinite(-590)+"
"); document.write(isFinite(90.33)+"
"); document.write(isFinite(NaN)+"
"); document.write(isFinite("Ovo je niz")+"
");

Brzi pregled

Imajte na umu: potpuni popis ugrađenih JavaScript funkcija možete pronaći u našem.

Lokalne i globalne varijable

Varijable stvorene unutar funkcija nazivaju se lokalne varijable. Takvim varijablama možete pristupiti samo unutar funkcija u kojima su definirane.

Nakon što funkcijski kod završi s izvođenjem, takve se varijable uništavaju. To znači da se varijable s istim imenom mogu definirati u različitim funkcijama.

Varijable koje su stvorene izvan funkcijskog koda nazivaju se globalne varijable; takvim varijablama može se pristupiti s bilo kojeg mjesta u kodu.

Ako unutar funkcije deklarirate varijablu bez var, ona također postaje globalna.

Globalne varijable se uništavaju tek nakon zatvaranja stranice.

//Deklariraj globalne varijable var1 i var2 var var1="var1 postoji"; var var2; funkcija func1() ( //Dodijelite var2 vrijednost unutar funkcije func1 var var2="var2 postoji"; ) //Iz druge funkcije, ispišite sadržaj varijable var1 i var2 na funkciju stranice func2() ( //Izlaz sadržaj varijable var1 document.write( var1 + "
"); //Izlaz sadržaja varijable var2 document.write(var2); )

Brzi pregled

Imajte na umu da će, kada se ispiše na zaslon, var2 imati praznu vrijednost jer func1 radi na lokalnoj "verziji" var2.

Korištenje anonimnih funkcija

Funkcije koje ne sadrže naziv kada su deklarirane nazivaju se anonimne.

Anonimne funkcije su u osnovi deklarirane da se ne pozivaju naknadno iz koda kao obične funkcije, već da se prosljeđuju drugim funkcijama kao parametar.

Funkcija arrMap(arr,func)( var res=novi niz; for (var i=0;i