Dažnai tą patį veiksmą reikia pakartoti daugelyje programos dalių.
Pavyzdžiui, reikia parodyti lankytojui gražų pranešimą, kai jis įeina į svetainę, kai jis išeina iš svetainės arba bet kur kitur.
Siekiant išvengti to paties kodo kartojimo daugelyje vietų, buvo sugalvotos funkcijos. Funkcijos - tai pagrindiniai programos blokai.
Jus jau matėte integruotų funkcijų pavyzdžių - tai alert(message)
, prompt(message, default)
ir confirm(question)
. Tačiau taip pat galima kurti savo funkcijas
Norėdami sukurti funkcijas, galime naudoti funkcijos deklaravimų.
Funkcijos deklaravimo pavyzdys:
function showMessage() {
alert( 'Labas visiems!' );
}
Pirmiausia įrašomas raktažodis funkcija
, po jo nurodomas funkcijos pavadinimas, po to skliausteliuose pateikiamas parametrų sąrašas, atskirtas kableliais (aukščiau pateiktame pavyzdyje jis yra tuščias), ir, galiausiai, funkcijos kodas, dar vadinamas “funkcijos turiniu”, riestiniuose skliaustuose.
function name(parameter1, parameter2, ... parameterN) {
...body...
}
Mūsų naująją funkciją galima iškviesti jos pavadinimu: showMessage()
.
Pavyzdžiui:
function showMessage() {
alert( 'Labas visiems!' );
}
*!*
showMessage();
showMessage();
*/!*
Kai mes iškviečiame showMessage()
, įvykdomas funkcijos kodas. Čia matysime pranešimą du kartus.
Šis pavyzdys aiškiai parodo vieną iš pagrindinių funkcijų tikslų - atsikratyti kodo dubliavimo.
Jei reikia pakeisti pranešimą arba jo išvedimo būdą, užteks pakeisti ją vienoje vietoje: funkcijoje, kuri išveda šį pranešimą.
Funkcijoje deklaruoti kintamieji matomi tik toje funkcijoje.
Pavyzdžiui:
function showMessage() {
*!*
let message = "Sveiki, aš esu JavaScript!"; // lokalinis kintamasis
*/!*
alert( message );
}
showMessage(); // Sveiki, aš esu JavaScript!
alert( message ); // <-- bus sukelta klaida, nes kintamasis matomas tik funkcijos viduje.
Funkcija turi prieigą prie išorinių kintamųjų, pavyzdžiui:
let *!*userName*/!* = 'Jonas';
function showMessage() {
let message = 'Sveiki, ' + *!*userName*/!*;
alert(message);
}
showMessage(); // Sveiki, Jonas
Funkcija turi pilną prieigą prie išorinių kintamųjų ir gali keisti jų vertę.
Pavyzdžiui:
let *!*userName*/!* = 'Jonas';
function showMessage() {
*!*userName*/!* = "Algirdas"; // (1) keičiame išorinio kintamojo vertę
let message = 'Sveiki, ' + *!*userName*/!*;
alert(message);
}
alert( userName ); // *!*Jonas*/!* prieš funkcijos iškvietimą
showMessage();
alert( userName ); // *!*Algirdas*/!*, vertė, pakeista funkcija
Išorinis kintamasis naudojamas tik tuo atveju, kai nėra lokalinio kintamojo.
Jei funkcijos viduje deklaruojame kintamąjį, kuris buvo deklaruotas už funkcijos ribų, išorinis kintamasis bus ignoruojamas. Pavyzdžiui, toliau pateiktame kode funkcija naudoja lokalinį kintamąjį userName
. Išorinis bus ignoruojamas:
let userName = 'Jonas';
function showMessage() {
*!*
let userName = "Algirdas"; // deklaruojame vietinį kintamąjį
*/!*
let message = 'Sveiki, ' + userName; // *!*Algirdas*/!*
alert(message);
}
// funkcija sukurs savo kintamąjį userName ir jį naudos
showMessage();
alert( userName ); // *!*Jonas*/!*, niekas nepasikeitė, funkcija nepakeitė išorinio kintamojo
Kintamieji, deklaruojami už visų funkcijų ribų, pvz., išorinis kintamasis `userName` aukščiau pateiktame kode, vadinami globaliaisiais.
Globalūs kintamieji matomi bet kurioje funkcijoje (nebent tose funkcijose yra to paties pavadinimo kintamųjų).
Patartina kuo mažiau naudoti globaliuosius kintamuosius. Šiuolaikiniame kode paprastai globalių kintamųjų būna nedaug arba jų iš viso nebūna. Nors kartais jos yra naudingos svarbiausiems projekto duomenims saugoti.
Naudodami parametrus (dar vadinamus funkcijos argumentais) funkcijai galime perduoti bet kokią informaciją.
Žemiau pateiktame pavyzdyje funkcijai perduodami du argumentai: from
ir text
.
function showMessage(*!*from, text*/!*) { // argumentai: from, text
alert(from + ': ' + text);
}
*!*
showMessage('Ana', 'Sveiki!'); // Ana: Sveiki! (*)
showMessage('Ana', "Kaip sekasi?"); // Ana: Kaip sekasi? (**)
*/!*
Kai funkcija iškviečiama eilutėse (*)
ir (**)
, perduotos vertės perkeliamos į lokalinius kintamuosius from
ir text
. Tada jie naudojami funkcijos turinyje.
Štai dar vienas pavyzdys: turime kintamąjį from
ir perduodame jį funkcijai. Atkreipkite dėmesį: funkcija pakeičia vertę from
, tačiau šis pokytis nėra matomas iš išorės. Funkcija visada gauna tik vertės kopiją:
function showMessage(from, text) {
*!*
from = '*' + from + '*'; // papuoškime from
*/!*
alert( from + ': ' + text );
}
let from = "Ana";
showMessage(from, "Hello"); // *Ana*: Sveiki
// "from" vertė lieka ta pati, funkcija pakeitė tik lokalinio kintamojo vertę
alert( from ); // Ana
Jei parametras nenurodytas, jo vertė tampa undefined
.
Pavyzdžiui, aukščiau pateiktą funkciją showMessage(from, text)
galima iškviesti su vienu argumentu:
showMessage("Ana");
Tai nesukels klaidos. Po tokio iškvietimo rašoma "*Ana*: undefined"
. Kvietime nenurodytas text
parametras, todėl laikoma, kad text === undefined
.
Jei norime nustatyti numatytąją text
parametro vertę, turime ją nurodyti po =
:
function showMessage(from, *!*text = "tekstas nepridėtas"*/!*) {
alert( from + ": " + text );
}
showMessage("Ana"); // Ana: tekstas nepridėtas
Dabar, jei text
parametras nenurodytas, jo vertė bus "tekstas nepridėtas"
.
Šiuo atveju "tekstas nepridėtas"
yra eilutė, tačiau vietoj jos gali būti sudėtingesnė išraiška, kuri apskaičiuojama ir priskiriama, kai nėra parametro. Pavyzdžiui:
function showMessage(from, text = anotherFunction()) {
// anotherFunction() bus vykdoma tik tuo atveju, jei text nebus perduotas
// rezultatas yra text vertė.
}
JavaScript kalba numatytieji parametrai apskaičiuojami kiekvieną kartą, kai funkcija iškviečiama be atitinkamo parametro.
Aukščiau pateiktame pavyzdyje `anotherFunction()` bus iškviečiama kiekvieną kartą, kai `showMessage()` bus iškviesta be `text` parametro.
Kartais tikslinga parametrams priskirti numatytąsias vertes ne funkcijos deklaravimo metu, o vėliau.
Mes galime patikrinti, ar parametras perduotas funkcijos vykdymo metu, palyginę jį su undefined
:
function showMessage(text) {
// ...
*!*
if (text === undefined) { // jei trūksta parametro
text = 'tekstas nepridėtas';
}
*/!*
alert(text);
}
showMessage(); // tekstas nepridėtas
...Arba naudodami operatorių ||
:
function showMessage(text) {
// jei text yra undefined arba falsy, priskirti vertę `tuščia`
text = text || 'tuščia';
...
}
Šiuolaikiniai JavaScript varikliai palaiko nulinio susiliejimo operatorių ??
, jis tinka geriau, jei dauguma neteisingų verčių, pvz., 0
, yra laikomos "normaliomis":
function showCount(count) {
// jei count yra undefined arba null, rodyti "nežinoma"
alert(count ?? "nežinoma");
}
showCount(0); // 0
showCount(null); // nežinoma
showCount(); // nežinoma
Funkcija gali grąžinti rezultatą, kuris bus perduotas ją iškvietusiam kodui.
Paprasčiausias pavyzdys - dviejų skaičių sudėjimo funkcija:
function sum(a, b) {
*!*return*/!* a + b;
}
let result = sum(1, 2);
alert( result ); // 3
return
direktyva gali būti bet kurioje funkcijos turinio vietoje. Kai tik vykdymas pasiekia šį tašką, funkcija sustoja ir vertė grąžinama ją iškvietusiam kodui (priskirta aukščiau nurodytam kintamajam result
).
Funkcijoje return
gali būti naudojamas kelis kartus, pavyzdžiui:
function checkAge(age) {
if (age >= 18) {
*!*
return true;
*/!*
} else {
*!*
return confirm('O tėvai leido?');
*/!*
}
}
let age = prompt('Kiek jums metų?', 18);
if ( checkAge(age) ) {
alert( 'Suteikta prieiga' );
} else {
alert( 'Prieiga uždrausta' );
}
Galima naudoti return
be vertės. Dėl to funkcija bus iš karto baigta.
Pavyzdžiui:
function showMovie(age) {
if ( !checkAge(age) ) {
*!*
return;
*/!*
}
alert( "Jums rodomas filmas" ); // (*)
// ...
}
Aukščiau pateiktame kode, jei checkAge(age)
grąžina false
, showMovie
neįvykdys alert
.
````smart header="Funkcijos rezultatas su tuščiu return
arba be jo yra `undefined`"
Jei funkcija negrąžina vertės, tai yra tas pats, lyg ji būtų grąžinusi undefined
vertę:
function doNothing() { /* tuščia */ }
alert( doNothing() === undefined ); // true
Tuščias return
yra analogiškas return undefined
:
function doNothing() {
return;
}
alert( doNothing() === undefined ); // true
````warn header="Niekada nedėkite eilutės laužimo tarp `return` ir jo vertės"
Ilgos išraiškos `return` atveju gali kilti pagunda ją pateikti keliose atskirose eilutėse, pvz., taip:
```js
return
(some + long + expression + or + whatever * f(a) + f(b))
```
Kodas nebus vykdomas, nes JavaScript interpretatorius po `return` įterpia kabliataškį. Jam šis kodas atrodys taip:
```js
return*!*;*/!*
(some + long + expression + or + whatever * f(a) + f(b))
```
Tai iš tikrųjų tampa tuščiu `return`.
Jei norime, kad grąžinimo išraiška apimtų kelias eilutes, turime ją pradėti toje pačioje eilutėje kaip ir `return`. Arba bent jau įterpti skliaustus, pvz., taip:
```js
return (
some + long + expression
+ or +
whatever * f(a) + f(b)
)
```
Tada viskas veiks taip, kaip buvo numatyta.
Funkcija - tai veiksmas. Todėl funkcijos pavadinimas dažniausiai yra veiksmažodis. Jis turi būti paprastas, tikslus ir apibūdinti funkcijos veiksmą taip, kad programuotojas, kuris skaitys kodą, teisingai suprastų, ką funkcija daro.
Paprastai veiksmažodžių priešdėliai vartojami bendram veiksmo pobūdžiui reikšti, o po jų eina paaiškinimas. Paprastai programuotojų komandos yra sudariusios susitarimus dėl šių priešdėlių reikšmių.
Pavyzdžiui, funkcijos, prasidedančios žodžiu "show"
, paprastai ką nors parodo.
Funkcijos, prasidedančios nuo...
"get…"
-- grąžina vertę,"calc…"
-- ką nors apskaičiuoja,"create…"
-- ką nors sukuria,"check…"
-- ką nors patikrina ir grąžina "Boolean" vertę ir t. t.
Tokių pavadinimų pavyzdžiai:
showMessage(..) // rodo pranešimą
getAge(..) // grąžina amžių
calcSum(..) // apskaičiuoja sumą ir grąžina rezultatą
createForm(..) // sukuria formą (ir dažniausiai ją grąžina).
checkPermission(..) // tikrina prieigą ir grąžina true/false
Dėl priešdėlių iš pirmo žvilgsnio aišku, ką funkcija daro ir kokią vertę gali grąžinti.
Funkcija turėtų atlikti tik tai, ką aiškiai nurodo jos pavadinimas. Tai turėtų būti vienas veiksmas.
Du nepriklausomi veiksmai paprastai reiškia dvi funkcijas, net jei jos turi būti iškviestos kartu (tokiu atveju galime sukurti trečią funkciją joms iškviesti).
Štai keletas pavyzdžių, kurie pažeidžia šią taisyklę:
- `getAge` -- būtų netinkamas pasirinkimas, jei funkcija išveda `alert` su amžiumi (turėtų tik grąžinti jį).
- `createForm` -- būtų netinkamas pasirinkimas, jei funkcija pakeistų dokumentą, pridėdama į jį formą (turėtų tik sukurti formą ir ją grąžinti).
- `checkPermission` -- būtų netinkamas pasirinkimas, jei funkcija rodytų pranešimą su tekstu "Prieiga suteikta / uždrausta" (turėtų tik atlikti patikrinimą ir grąžinti jo rezultatą).
Šiuose pavyzdžiuose naudotos bendrinės priešdėlių reikšmės. Žinoma, savo komandoje galite susitarti ir dėl kitų reikšmių, tačiau jos paprastai nedaug skiriasi nuo bendrųjų. Bet kokiu atveju jūs ir jūsų komanda turėtumėte tiksliai suprasti, ką reiškia priešdėlių, ką funkcija gali ir ko negali su juo daryti.
Labai dažnai naudojami funkcijų pavadinimai kartais būna itin trumpi.
Pavyzdžiui, [jQuery](http://jquery.com) framework'e yra funkcija, pavadinta `$`. [Lodash](http://lodash.com/) bibliotekoje pagrindinę funkciją žymi vardas `_`.
Tai yra išimtys. Apskritai funkcijų pavadinimai turėtų būti vidutiniškai trumpi ir apibūdinti funkcijų veiksmus.
Funkcijos turėtų būti trumpos ir atlikti tik vieną veiksmą. Jei funkcija yra didelė, ją tikslinga suskaidyti į kelias mažesnes. Kartais nelengva laikytis šios taisyklės, tačiau ji tikrai gera.
Nedidelės funkcijos ne tik palengvina testavimą ir derinimą -- pats tokių funkcijų egzistavimas atlieka gerų komentarų vaidmenį!
Pavyzdžiui, palyginkime žemiau pateiktas dvi showPrimes(n)
funkcijas. Kiekviena iš jų išveda paprastąjį skaičių iki n.
Pirmame variante naudojama nextPrime
žyma:
function showPrimes(n) {
nextPrime: for (let i = 2; i < n; i++) {
for (let j = 2; j < i; j++) {
if (i % j == 0) continue nextPrime;
}
alert( i ); // paprastas skaičius
}
}
Antrajame variante naudojama papildoma funkcija isPrime(n)
, skirta patikrinti, ar yra paprastųjų skaičių:
function showPrimes(n) {
for (let i = 2; i < n; i++) {
*!*if (!isPrime(i)) continue;*/!*
alert(i); // paprastas skaičius
}
}
function isPrime(n) {
for (let i = 2; i < n; i++) {
if ( n % i == 0) return false;
}
return true;
}
Antrąjį variantą lengviau suprasti, ar ne? Vietoj kodo dalies matome veiksmo pavadinimą (isPrime
). Kartais programišiai šį kodą vadina savaiminiu dokumentavimu.
Todėl galima kurti funkcijas, net jei neplanuojame jų pakartotinai naudoti. Tokios funkcijos struktūrizuoja kodą ir daro jį aiškesnį.
Funkcijos deklaravimas atrodo taip:
function name(kableliais, atskirti, parametrai) {
/* kodas */
}
- Perduotos vertės nukopijuojamos į funkcijos parametrus ir tampa lokaliniais kintamaisiais.
- Funkcijos turi prieigą prie išorinių kintamųjų. Tačiau tai veikia tik iš vidaus į išorę. Kodas, esantis už funkcijos ribų, neturi prieigos prie jos lokalinių kintamųjų.
- Funkcija gali grąžinti vertę. Jei funkcija nieko negrąžina, rezultatas yra
undefined
.
Kad kodas būtų švaresnis ir aiškesnis, rekomenduojama naudoti lokalinius kintamuosius ir funkcijų parametrus, o ne išorinius kintamuosius.
Funkcija, kuri gauna parametrus, dirba su jais ir grąžina rezultatą, yra daug aiškesnė nei funkcija, kuri iškviečiama be parametrų, bet keičia išorinius kintamuosius, o tai gali turėti šalutinį poveikį.
Funkcijos pavadinimo pasirinkimas:
- Funkcijos pavadinimas turėtų būti aiškus ir aiškiai išreikšti jos paskirtį. Kai pamatysite, kad ji iškviečiama kode, iš karto suprasite, ką ji daro ir ką grąžina.
- Funkcija - tai veiksmas, todėl jos pavadinimas paprastai yra veiksmažodis.
- Yra daug bendrinių priešdėlių, pvz:
create…
,show…
,get…
,check…
ir t. t. Naudokite juos kaip užuominas, paaiškinančias, ką funkcija daro.
Funkcijos yra pagrindinės skriptų sudedamosios dalys. Apžvelgėme tik JavaScript funkcijų pagrindus, tačiau jau galime jas kurti ir naudoti. Tai tik kelionės pradžia. Prie funkcijų grįšime daug kartų ir jas vis giliau nagrinėsime.