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 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 upozorenja. 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!"); ); praviti 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. 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 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. Ovdje jednostavan program s više 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; rezultat prom. = 1; for (broj prom. = 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 malu izmjenu, 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. Dobra opcija mentalni model je zamisliti da funkcija zamrzava kod u svom tijelu i omotava ga u pakiranje. 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, postoji problem s ovom implementacijom: normalno okruženje JavaScript je 10 puta sporiji 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, je da ne brinete o performansama dok niste posve 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 iskusan programer odmah vidi 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 koje. 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 pokušava oboje moguće načine, 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 ponavljajuću funkcionalnost i usklađujemo je dobro ime i staviti ga 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. Ispostavilo se da dok petlje dodajte vodeće nule brojevima dok ne dobijete niz 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?

Naravno da je moguće. 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 ili jednostavnu funkciju koja dodaje broj nulama do tri mjesta ili sofisticiranu funkciju Opća namjena za formatiranje brojeva, podržavanje razlomaka, negativnih brojeva, poravnavanje točaka, punjenje različitim znakovima 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 min funkciju koja uzima dva argumenta i vraća njihov minimum.

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 posljednji 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 izvršavaju jednu konkretan 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 prenijeti u funkciju. 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 bit će detaljnije riječi kasnije u ovom udžbeniku.

// 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 proslijeđujući joj varijablu a plus(a); document.write("Vrijednost varijable nakon poziva funkcije: "+a+"
");

Brzi pogled

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 pogled

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 pogled

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 pogled

Bilješka: puni popis ugrađeni JavaScript funkcije Možete ga 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 se varijablama može 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() ( //Ispis sadržaj varijable var1 document.write( var1 + "
"); //Izlaz sadržaja varijable var2 document.write(var2); )

Brzi pogled

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