JavaScript funkcije. Ekspresivni JavaScript: Javascript funkcije koje vraćaju više vrijednosti iz funkcije

Ljudi misle da je informatika umjetnost za genije. U stvarnosti je obrnuto - samo puno ljudi pravi stvari koje stoje jedna na drugoj, kao da pravi zid od sitnih kamenčića.

Donald Knuth

Već ste vidjeli pozive funkcijama kao što je alert . Funkcije su kruh i puter JavaScript programiranja. Ideja da se dio programa omota i pozove kao varijabla je vrlo popularna. To je alat za strukturiranje velikih programa, smanjenje ponavljanja, imenovanje potprograma i izolaciju potprograma jedan od drugog.

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.

Prosečna odrasla osoba koja govori ruski zna oko 10.000 reč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 svoje riječi kako bismo izbjegli nepotrebno ponavljanje.

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

Var square = function(x) (vraćanje x * x; ); console.log(kvadrat(12)); // → 144

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

Funkcija može imati nekoliko parametara ili uopće ne imati. U sljedećem primjeru makeNoise nema listu parametara, ali power ima dva:

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

Neke funkcije vraćaju vrijednost, poput snage i kvadrata, druge ne, kao makeNoise, koji proizvodi samo nuspojave. Naredba return specificira vrijednost koju vraća funkcija. Kada obrada programa dostigne ovu instrukciju, odmah izlazi iz funkcije i vraća ovu vrijednost na mjesto u kodu iz kojeg je funkcija pozvana. return bez izraza vraća nedefinisano.

Parametri i opseg Parametri funkcije su iste varijable, ali se njihove početne vrijednosti postavljaju kada se funkcija pozove, a ne u njenom kodu.

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

Ova lokacija varijable primjenjuje se samo na parametre i varijable kreirane unutar funkcija. Varijable definirane izvan bilo koje funkcije nazivaju se globalnim jer su vidljive u cijelom programu. Takvim varijablama možete pristupiti i unutar funkcije, osim ako ne deklarirate lokalnu varijablu s istim imenom.

Sljedeći kod to ilustruje. Definira i poziva dvije funkcije koje dodjeljuju vrijednost varijabli x. Prvi ga deklarira kao lokalni, čime se mijenja samo lokalna varijabla. Drugi se ne deklarira, tako da se rad sa x unutar funkcije odnosi na globalnu varijablu x definiranu na početku primjera.

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

Ovo ponašanje pomaže u sprječavanju slučajnih interakcija između funkcija. Kada 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 ponovo koristili varijablu, naišli biste na čudne efekte kada kod treće strane pokvari vrijednosti vaše varijable. Tretiranjem funkcijskih lokalnih varijabli tako da one postoje samo unutar funkcije, jezik omogućava rad sa funkcijama kao da su odvojeni mali univerzumi, što vam omogućava 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 lokaliteta.

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

Var pejzaž = funkcija () ( var rezultat = ""; var stan = funkcija (veličina) ( za (broj var = 0; broj< 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 je definira. Ali oni ne mogu vidjeti jedni druge varijable brojanja jer su varijable jedne funkcije izvan opsega druge. A okruženje izvan funkcije pejzaža ne vidi nijednu 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 lokacijom na kojoj je funkcija deklarirana u programu. Sve varijable iz blokova koji okružuju definiciju funkcije su vidljive - uključujući i one definirane na najvišoj razini u glavnom programu. Ovaj pristup opsegu naziva se leksičkim.

Ljudi koji su proučavali druge programske jezike mogu pomisliti da svaki blok zatvoren u vitičaste zagrade stvara vlastito lokalno okruženje. Ali u JavaScript-u samo funkcije kreiraju opseg. Možete koristiti samostojeće blokove:

Var nešto = 1; ( var something = 2; // Uradi nešto sa promenljivom nešto... ) // Izašao iz bloka...

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

Ako vam se ovo čini čudnim, niste jedini koji tako mislite. U verziji JavaScript 1.7 pojavio se ključna riječ let, koji radi kao var, ali stvara varijable koje su lokalne za bilo koji blok, a ne samo funkciju.

Funkcije kao vrijednosti Nazivi funkcija se obično koriste kao nazivi za dio programa. Takva varijabla se postavlja jednom i ne mijenja se. Dakle, lako je pobrkati funkciju i njeno ime.

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

Var launchMissiles = function(value) (missileSystem.launch("ili!"); ); if (safeMode) launchMissiles = function(value) (/* cancel */);

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

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

Funkcija square(x) (vraćanje x * x; )

Ovo je deklaracija funkcije. Naredba definira kvadratnu varijablu i dodjeljuje joj datu funkciju. Zasada je dobro. Postoji samo jedna zamka u takvoj definiciji.

Console.log("Budućnost kaže:", future()); funkcija future() ( vrati "Još uvijek nemamo letećih automobila."; )

Ovaj kod radi iako je funkcija deklarirana ispod koda koji ga koristi. To je zato što deklaracije funkcija nisu dio normalnog izvršavanja programa odozgo prema dolje. Oni su "premešteni" na vrh svog opsega i mogu se pozvati bilo kojim kodom u tom opsegu. Ponekad je ovo zgodno jer možete pisati kod onim redoslijedom koji ima najviše smisla bez brige da ćete morati definirati sve gore navedene funkcije gdje se koriste.

Što se događa ako deklaraciju funkcije postavimo unutar uvjetnog bloka ili petlje? Ne morate to da radite. Istorijski gledano, različite platforme za pokretanje JavaScript su drugačije rješavale takve slučajeve, a trenutni jezički standard to zabranjuje. Ako želite da se vaši programi pokreću sekvencijalno, koristite deklaracije funkcija samo unutar drugih funkcija ili glavnog programa.

Funkcija example() ( funkcija a() () // Normalno ako (nešto) ( funkcija b() () // Ay-yay-yay! ))

Stack poziva Korisno je detaljnije pogledati kako redoslijed izvršenja funkcionira s funkcijama. Evo jednostavan program sa višestrukim pozivima funkcija:

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

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

Ovo se može shematski prikazati ovako:

Top greet console.log greet top console.log top

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

Mjesto na kojem računar pamti kontekst naziva se stek. Svaki put kada se funkcija pozove, trenutni kontekst se gura na vrh steka. Kada se funkcija vrati, ona izbacuje gornji kontekst iz steka i koristi ga za nastavak rada.

Stack memorija zahtijeva memorijski prostor. Kada stek postane prevelik, računar će prestati da se izvršava i reći će nešto poput „preklapanje steka“ ili „previše rekurzije“. Sljedeći kod to demonstrira - postavlja kompjuteru vrlo složeno pitanje, što dovodi do beskonačnih skokova između dvije funkcije. Tačnije, to bi bili beskonačni skokovi da kompjuter ima beskonačan stog. U stvarnosti, stek se prepuni.

Funkcija chicken() (vrati jaje(); ) funkcija egg() (vrati chicken(); ) console.log(chicken() + " je prvi."); // → ??

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

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

Zvanično, funkcija uzima jedan argument. Međutim, kada je ovako izazvana, ne žali se. Ona ignorira ostale argumente i pokazuje "Zdravo".

JavaScript je vrlo specifičan u pogledu broja argumenata koji se prosljeđuju funkciji. Ako prenesete previše, dodatni će biti zanemareni. Premalo i onima koji nedostaju bit će dodijeljena vrijednost nedefinirana.

Loša strana ovog pristupa je ta što je moguće - pa čak i vjerovatno - funkciji proslijediti pogrešan broj argumenata bez da se itko žali na to.

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

Snaga funkcije (baza, eksponent) ( if (eksponent == nedefinisan) eksponent = 2; var rezultat = 1; for (broj var = 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 tačan broj argumenata koji su joj proslijeđeni. Ovo je korisno jer... omogućava vam da kreirate funkciju 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 kreiraju 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 ilustruje ovo pitanje. Deklariše funkciju wrapValue, koja kreira lokalnu varijablu. Zatim vraća funkciju koja čita ovu lokalnu varijablu i vraća njenu vrijednost.

Funkcija wrapValue(n) ( var localVariable = n; return function() ( return localVariable; ); ) var wrap1 = wrapValue(1); var wrap2 = wrapValue(2); console.log(wrap1()); // → 1 console.log(wrap2()); // → 2

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

Ova sposobnost 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 životnim vijekovima, već vam omogućava i kreativno korištenje funkcija.

Uz malu modifikaciju, pretvaramo naš primjer u funkciju koja množi brojeve sa bilo kojim brojem.

Funkcija množitelj(faktor) (povratna funkcija(broj) (povratni broj * faktor; ); ) var double = množitelj(2); console.log(twice(5)); // → 10

Zasebna varijabla kao što je localVariable iz primjera wrapValue više nije potrebna. Pošto je parametar sam po sebi lokalna varijabla.

Biće potrebna praksa da počnete da razmišljate na ovaj način. Dobra opcija mentalni model je zamisliti da funkcija zamrzava kod u svom tijelu i umotava ga u pakovanje. Kada vidite funkciju return(...) (...), zamislite je kao kontrolnu tablu za zamrznuti dio koda za kasniju upotrebu.

U našem primjeru, množitelj vraća zamrznuti dio koda, koji pohranjujemo u promjenljivu double. Posljednji red poziva funkciju sadržanu u varijabli, što uzrokuje aktiviranje pohranjenog koda (povratni broj * faktor;). I dalje ima pristup varijabli faktora koja je definirana prilikom pozivanja množitelja, a također ima pristup argumentu proslijeđenom tokom odmrzavanja (5) kao numeričkom parametru.

Rekurzija Funkcija može pozvati samu sebe sve dok vodi računa da ne prelije stek. Ova funkcija se naziva rekurzivna. Evo primjera alternativne implementacije eksponencijacije:

Funkcija power(base, exponent) ( if (exponent == 0) return 1; else return base * power(base, exponent - 1); ) console.log(power(2, 3)); // → 8

Otprilike ovako matematičari definiraju eksponencijaciju, a možda ovo opisuje koncept elegantnije od ciklusa. Funkcija se poziva mnogo puta s različitim argumentima kako bi postigla višestruko množenje.

Međutim, postoji problem sa ovom implementacijom: normalno okruženje JavaScript je 10 puta sporiji od verzije sa petljom. Prolazak kroz petlju je jeftiniji od pozivanja funkcije.

Dilema između brzine i elegancije je prilično zanimljiva. Postoji određeni jaz između pogodnosti za ljude i pogodnosti za mašine. Svaki program se može ubrzati tako što će biti veći i složeniji. Od programera se traži da pronađe odgovarajući balans.

U slučaju prve eksponencije, neelegantna petlja je prilično jednostavna i jasna. Nema smisla zameniti ga rekurzijom. Međutim, često se programi bave tako složenim konceptima da se želi smanjiti efikasnost povećanjem čitljivosti.

Osnovno pravilo, koje je ponovljeno više puta, i sa kojim se potpuno slažem, je da ne brinete o performansama dok niste potpuno sigurni da se program usporava. Ako je tako, pronađite dijelove koji traju najduže i zamijenite eleganciju za efikasnost.

Naravno, ne bismo trebali odmah potpuno zanemariti performanse. U mnogim slučajevima, kao i kod eksponencijalnosti, ne postižemo mnogo jednostavnosti od elegantnih rješenja. Ponekad iskusan programer odmah vidi da jednostavan pristup nikada neće biti dovoljno brz.

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

Ali rekurzija nije uvijek samo manje efikasna alternativa petljama. Neke probleme je lakše riješiti rekurzijom. Najčešće se radi o obilasku nekoliko grana drveta, od kojih se svaka može granati.

Evo zagonetke: možete dobiti beskonačan broj brojeva počevši od broja 1, a zatim ili sabiranjem 5 ili množenjem sa 3. Kako da napišemo funkciju koja, zadat broj, pokušava pronaći slijed sabiranja i množenja koji vode do određenog broja? Na primjer, broj 13 se može dobiti tako da prvo pomnožite 1 sa 3, a zatim dvaput dodate 5. A broj 15 se nikako ne može dobiti na ovaj način.

Rekurzivno rješenje:

Funkcija findSolution(target) ( funkcija find(start, historija) ( if (start == cilj) vraća povijest; inače if (start > target) vraća null; inače vraća find(start + 5, "(" + historija + " + 5)") || find(start * 3, "(" + istorija + " * 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 koje. Ne očekujem da odmah shvatite kako program radi. Ali hajde da shvatimo ovu sjajnu vežbu rekurzivnog razmišljanja.

Unutrašnja funkcija find vrši rekurziju. Potrebna su dva argumenta - trenutni broj i niz koji sadrži zapis kako smo došli do ovog broja. I vraća ili string koji pokazuje naš niz koraka, ili null.

Da bi to učinila, funkcija izvodi jednu od tri radnje. Ako je zadati broj jednak cilju, onda je trenutna priča upravo način da se to postigne, pa se vraća. Ako je dati broj veći od cilja, nema smisla nastaviti množenje i sabiranje, jer će se samo povećavati. A ako još nismo došli do cilja, funkcija pokušava oboje mogući načini, počevši od datog broja. Ona se priziva dva puta, jednom sa svakom metodom. Ako prvi poziv ne vrati null, vraća se. U drugom slučaju, drugi se vraća.

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

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

Udubljenje pokazuje dubinu steka poziva. Prvi put, funkcija find se poziva dva puta da provjeri rješenja počevši od (1 + 5) i (1 * 3). Prvi poziv traži rješenje koje počinje sa (1 + 5) i koristi rekurziju za provjeru svih rješenja koja proizvode broj manji ili jednak traženom broju. Ne pronalazi ga i vraća null. Tada operator || i prelazi na poziv funkcije koji ispituje opciju (1 * 3). Ovdje imamo sreće, jer u trećem rekurzivnom pozivu dobijamo 13. Ovaj poziv vraća string, a svaki od || usput prolazi ovu liniju više, što rezultira vraćanjem rješenja.

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

Prvi je da napišete sličan kod nekoliko puta. Ovo treba izbjegavati - više koda znači više prostora za greške i više materijala za čitanje za one koji pokušavaju razumjeti program. Dakle, uzimamo ponavljajuću funkcionalnost i usklađujemo je dobro ime i stavi ga u funkciju.

Drugi način je da otkrijete potrebu za nekom novom funkcionalnošću koja je dostojna da bude stavljena u posebnu funkciju. Počinjete s imenom funkcije, a zatim pišete njeno tijelo. Možete čak početi pisanjem koda koji koristi funkciju prije nego što je sama funkcija definirana.

Koliko vam je teško da imenujete funkciju pokazuje koliko dobro razumete njenu funkcionalnost. Uzmimo primjer. Moramo napisati program koji ispisuje dva broja, broj krava i pilića na farmi, nakon čega slijede riječi “krave” i “kokoške”. Brojevima ispred morate dodati nule tako da svaki zauzima tačno tri pozicije.

007 Krave 011 Pilići

Očigledno, potrebna nam je funkcija sa dva argumenta. Počnimo s kodiranjem.
// ispis funkcije FarmInventory printFarmInventory(krave, kokoši) ( 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 stringu dodamo .length, dobićemo njegovu dužinu. Ispostavilo se da while petlje dodajte vodeće nule brojevima dok ne dobijete niz od 3 karaktera.

Spremni! Ali baš kad smo hteli da pošaljemo šifru farmeru (uz pozamašan ček, naravno), on nas zove i kaže da ima svinje na svojoj farmi, i da li bismo mogli da dodamo prikaz broja svinja na program?

Naravno da je moguće. Ali kada počnemo da kopiramo i lijepimo kod iz ova četiri reda, shvatimo da moramo stati i razmisliti. Mora postojati bolji način. Trudimo se da unapredimo program:

// izlaz SA dodavanjem nula i oznaka funkcije printZeroPaddedWithLabel(broj, oznaka) ( var numberString = String(broj); 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);

Works! Ali naziv printZeroPaddedWithLabel je malo č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 nultu funkciju zeroPad(broj, širina) ( var string = String(broj); 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 sa lijepim, jasnim imenom zeroPad čini kod lakšim za razumijevanje. 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 funkcije trebale biti pametne i svestrane? Možemo napisati ili jednostavnu funkciju koja popunjava broj nulama do tri pozicije ili sofisticiranu funkciju opće namjene za formatiranje brojeva, podršku razlomaka, negativnih brojeva, poravnanje tačaka, popunjavanje različitim znakovima itd.

Dobro pravilo je da dodate samo funkcionalnost za koju znate da će biti korisna. Ponekad je primamljivo kreirati okvire opšte namene za svaku malu potrebu. Oduprite mu se. Nikada nećete završiti posao, samo ćete na kraju napisati gomilu koda koji niko 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, moguće je i kombinirati ova svojstva u jednoj funkciji.

Prva pomoćna funkcija u primjeru farme, printZeroPaddedWithLabel, poziva se jer ima nuspojavu: ispisuje string. Drugi, zeroPad, zbog povratne vrijednosti. I nije slučajno što je druga funkcija korisna češće od prve. Funkcije koje vraćaju vrijednosti lakše je kombinirati jedna s drugom nego funkcije koje proizvode nuspojave.

Čista funkcija je posebna vrsta funkcije koja vraća vrijednost koja ne samo da nema nuspojave, već i ne ovisi o nuspojavama ostatka koda - na primjer, ne radi s globalnim varijablama koje bi mogle biti slučajno promenio negde drugde. Čista funkcija, kada se pozove s istim argumentima, vraća isti rezultat (i ne radi ništa drugo) - što je prilično lijepo. Lako je sa njom raditi. Poziv takve funkcije može se mentalno zamijeniti rezultatom njenog rada, bez promjene značenja koda. Kada želite da testirate takvu funkciju, možete je jednostavno pozvati i biti sigurni da će, ako radi u datom kontekstu, raditi u bilo kom kontekstu. Manje čiste funkcije mogu dati različite rezultate ovisno o mnogim faktorima i imati nuspojave koje je teško testirati i uzeti u obzir.

Međutim, ne biste se trebali stidjeti pisati funkcije koje nisu potpuno čiste, ili započeti sakralno čišćenje koda takvih funkcija. Nuspojave su često korisne. Ne postoji način da se napiše čista verzija funkcije console.log, a ova funkcija je prilično korisna. Neke operacije je lakše izraziti upotrebom nuspojava.

Sažetak Ovo poglavlje vam je 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 što ćete joj dodijeliti poziv funkcije.

Ključ za razumijevanje funkcija je lokalni opseg. Parametri i varijable deklarirane unutar funkcije su lokalne za nju, ponovno se kreiraju svaki put kada se ona pozove i nisu vidljive izvana. Funkcije deklarirane unutar druge funkcije imaju pristup njenom opsegu.

Vrlo je korisno razdvojiti različite zadatke koje izvršava program u funkcije. Ne morate se ponavljati; funkcije čine kod čitljivijim tako što ga dijele na smislene dijelove, baš kao što poglavlja i dijelovi knjige pomažu organiziranju redovnog teksta.

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

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

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

Nula je paran.
Jedinica je čudna.
Bilo koji broj N ima isti paritet kao N-2.

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

Testirajte na 50 i 75. Pokušajte dati -1. Zašto se ponaša ovako? Da li je 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 ovo popravite?

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

Brojanje pasulja.

Broj karaktera N niza se može dobiti dodavanjem .charAt(N) (“string”.charAt(5)) – na sličan način kao i dobijanje dužine stringa koristeći .length. Povratna vrijednost će biti niz koji se sastoji od jednog znaka (na primjer, “k”). Prvi znak niza ima poziciju 0, što znači da će posljednji karakter imati poziciju string.length – 1. Drugim riječima, niz od dva znaka ima dužinu 2, a njegove pozicije znakova će biti 0 i 1.

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

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

Funkcije su jedan od najvažnijih gradivnih blokova koda u JavaScriptu.

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

Kod stavljen u funkciju će se izvršiti samo nakon eksplicitnog poziva ove funkcije.

Deklaracija funkcije

1. Sintaksa:

//Deklaracija funkcije functionFunctionname(ln1, ln2)( Function code) //Pozivanje funkcije functionFunctionname(ln1,lr2);

2. Sintaksa:

//Deklaracija funkcije var function name=function(ln1, ln2)(Kôd funkcije) //Pozivanje funkcije funkcije name(ln1,lr2);

functionname specificira ime funkcije. Svaka funkcija na stranici mora imati jedinstveno ime. Ime funkcije mora biti navedeno latiničnim slovima i ne smije početi brojevima.

ln1 i ln2 su varijable ili vrijednosti koje se mogu prenijeti u funkciju. Neograničen broj varijabli može biti proslijeđen 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 imena funkcija u JavaScript-u razlikuju velika i mala slova.

Primjer JavaScript funkcije

Funkcija messageWrite() u primjeru ispod bit će izvršena tek nakon što se klikne na dugme.

Imajte na umu da ovaj primjer koristi događaj onclick. JavaScript događaji biće detaljno razmotreno kasnije u ovom udžbeniku.

// Funkcija upisuje tekst u funkciju stranice messageWrite() ( document.write("Ovaj tekst je napisan na stranicu koristeći JavaScript!"); )

Prenošenje varijabli u funkcije

Funkcijama možete proslijediti neograničen broj varijabli.

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

/* Hajde da definiramo 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 tako što ćete joj proslijediti varijablu a plus(a); document.write("Vrijednost varijable nakon poziva funkcije: "+a+"
");

Brzi pregled

Da pristupite globalnoj varijabli iz funkcije, a ne iz njene 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

naredba povratka

Pomoću naredbe 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=" + suma(10,4) + "
");

Brzi pregled

Ugrađene funkcije

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

Na primjer, ugrađena isFinite funkcija vam omogućava da provjerite da li je proslijeđena vrijednost ispravan 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

Bilješka: puna lista ugrađen JavaScript funkcije Možete ga pronaći u našoj.

Lokalne i globalne varijable

Varijable kreirane unutar funkcija nazivaju se lokalnim varijablama. Takvim varijablama možete pristupiti samo unutar funkcija u kojima su definirane.

Nakon što kod funkcije završi izvršavanje, takve varijable se uništavaju. To znači da se varijable s istim imenom mogu definirati u različitim funkcijama.

Varijable koje se kreiraju izvan koda funkcije nazivaju se globalnim varijablama; takvim varijablama se može pristupiti s bilo kojeg mjesta u kodu.

Ako deklarišete promenljivu bez var unutar funkcije, ona takođe postaje globalna.

Globalne varijable se uništavaju tek nakon što se stranica zatvori.

//Objavi globalne varijable var1 i var2 var var1="var1 postoji"; var var2; function func1() ( //Dodijeli var2 vrijednost unutar funkcije func1 var var2="var2 postoji"; ) //Iz druge funkcije, izbaci sadržaj varijable var1 i var2 u 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 kada se ispiše na ekran, var2 će imati praznu vrijednost jer func1 radi na lokalnoj "verziji" var2.

Korištenje anonimnih funkcija

Funkcije koje ne sadrže ime kada su deklarirane nazivaju se anonimnim.

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