JavaScript funkciju veidi un pielietošana.

Funkcijas ir viena no JavaScript pamata sastāvdaļām. Funkcija JavaScript valodā ir līdzīga procedūrai – komplektam izteikumiem, kas veic uzdevumu vai aprēķina vērtību, bet lai procedūra kvalificētos kā funkcija, tā ir jāpieņem kāda ievade un jāatgriež rezultāts, kur ir skaidra saikne starp ievadi un rezultātu. Lai izmantotu funkciju, to ir jādefinē kādā scoupā, no kura vēlaties to izsaukt.


Funkciju definēšana

Funkciju deklarācijas

Funkcijas definīcija (arī saukta par funkcijas deklarāciju vai funkcijas izteikumu) sastāv no atslēgvārda function , kurai seko:

  • Funkcijas nosaukums.
  • Funkcijas parametru saraksts, iekļauts iekavās un atdalīts ar komatiem.
  • JavaScript izteikumi, kas definē funkciju, iekļauti iekavās, { //}.

Piemeram, sekojošais kods definē vienkāršu funkciju ar nosaukumu square :

function square(number) {
return number * number;
}

Funkcija square pieņem vienu parametru, ko sauc par number. Funkcija sastāv no vienas izteiksmes, kas liek, tai atgriezt funkcijas parametru (tātad, number) reizināt ar sevi pašu. Izteiksme return norāda funkcijā atgriezto vērtību:

return number * number;

Parametri tiek pārsūtīti uz funkcijām galvenokārt ar vērtību – tāpēc, ja funkcijas zonā tiek piešķirta pilnīgi jauna vērtība parametram, kas tika pārsūtīts uz funkciju, šī izmaiņa netiek atspoguļota globāli vai kodā, kas izsauc šo funkciju.

Ja kā parametrs tiek pārsūtīts objekts, un funkcija maina objekta īpašības, šī izmaiņa ir redzama ārpus funkcijas, kā parādīts sekojošā piemērā:

function myFunc(theObject) {
theObject.make = 'Toyota';
}

const mycar = {
make: 'Honda',
model: 'Accord',
year: 1998,
};

// x saņem vērtību "Honda"
const x = mycar.make;

// fubkcija veic izmaiņas
myFunc(mycar);
// y saņem vērtību "Toyota"
const y = mycar.make;

Ja kā parametrs tiek pārsūtīts masīvs, un funkcija maina masīvā kādu no vērtībām, šīs izmaiņas ir redzamas ārpus funkcijas, kā parādīts sekojošā piemērā:

function myFunc(theArr) {
theArr[0] = 30;
}

const arr = [45];

console.log(arr[0]); // 45
myFunc(arr);
console.log(arr[0]); // 30

Funkcijas izteiksmes

Kaut arī funkcijas deklarācija iepriekš ir sintaktiski teikums, funkcijas var izveidot arī ar funkcijas izteiksmes. Šāda funkcija var būt anonīma; tai nav jābūt nosaukumam. Piemēram, funkcija square varēja būt arī definēta šādi:

const square = function (number) {
return number * number;
}
const x = square(4); // x gets the value 16

Tomēr funkcijas izteiksmei labāk nodrošināt nosaukumu. Nosaukuma nodrošināšana ļauj funkcijai atsaukties uz sevi, un tā arī kļūst vieglāk identificējama un lasāma atkļūdošanas laikā:

const factorial = function fac(n) {
return n < 2 ? 1 : n * fac(n - 1);
}

console.log(factorial(3))

Funkcijas izteiksmes ir ērtas, ja funkciju pārsūta kā argumentu citai funkcijai. Sekojošais piemērs parāda map funkciju, kurai kā pirmajam argumentam jāsaņem funkcija, bet kā otrajam argumentam – masīvs:

function map(f, a) {
const result = new Array(a.length);
for (let i = 0; i < a.length; i++) {
result[i] = f(a[i]);
}
return result;
}

Sekojošajā kodā funkcija saņem funkciju, kuru definē funkcijas izteiksme, un izpilda to par katru masīva elementu, kuru saņem kā otro argumentu:

function map(f, a) {
const result = new Array(a.length);
for (let i = 0; i < a.length; i++) {
result[i] = f(a[i]);
}
return result;
}

const f = function (x) {
return x * x * x;
}

const numbers = [0, 1, 2, 5, 10];
const cube = map(f, numbers);
console.log(cube);

Funkcija atgriež: [0, 1, 8, 125, 1000]. JavaScript valodā funkcija var tikt definēta, balstoties uz noteiktu nosacījumu. Piemēram, sekojošā funkcijas definīcija definē myFunc tikai tad, ja num ir vienāds ar 0:

let myFunc;
if (num === 0) {
myFunc = function (theObject) {
theObject.make = 'Toyota';
}
}

Latviski tas izskatītos šādi: “Papildus funkciju definēšanai, kā tas ir aprakstīts šeit, jūs varat izmantot arī funkcijas konstruktoru, lai izveidotu funkcijas no vērtības virknes tieši izpildes laikā, līdzīgi kā ar eval().

Metode ir funkcija, kas ir objekta īpašība.

Funkciju izsaukšana

Izsaucot funkciju, tiek izpildītas norādītās darbības ar norādītajiem parametriem. Piemēram, ja jūs definējat funkciju “square”, to varētu izsaukt šādi:

square(10)

Tātad, funkciju definēšana nosauc funkciju un norāda, kādas darbības ir jāveic, kad funkcija tiek izsaukta. Funkciju izsaukšana ir tā, kur tiek faktiski izpildītas norādītās darbības ar norādītajiem parametriem.

Iepriekš minētais “statements” izsauc funkciju ar argumentu 5. Funkcija izpilda tās teikumus un atgriež vērtību 25.

Funkcijām jābūt skoupā, kad tiek izsauktas, bet funkcijas deklarāciju var “hoist” (parādīt zem izsaukuma kodā). Funkcijas deklarācijas skoups ir tā funkcija, kurā tā ir deklarēta (vai visas programmas skoups, ja tā ir deklarēta augšējā līmenī).

Funkcijas argumenti nav ierobežoti tikai ar simbolu virknēm un skaitļiem. Jūs varat nodot visus objektus funkcijai. Funkcija showProps() (definēta “Strādājot ar objektiem”) ir piemērs funkcijai, kas ņem objektu kā argumentu.

Funkcija var izsaukt sevi. Piemēram, šeit ir funkcija, kas rekursīvi aprēķina faktoriāļus:

function factorial(n) { 
if (n === 1) { return 1; } 
else { return n * factorial(n - 1); 
} 
}

Šī funkcija izsauks sevi atkārtoti, līdz sasniedz vērtību 1, un pēc tam sāks atgriezt faktoriāļu vērtības. Piemēram, ja izsauktu funkciju factorial(4), tā atgrieztu

vērtību 24 (4 * 3 * 2 * 1).

Tad jūs varētu aprēķināt faktoriāļus no 1 līdz 5, šādi:

const a = factorial(1); // a saņem vērtību 1
const b = factorial(2); // b saņem vērtību 2
const c = factorial(3); // c saņem vērtību 6
const d = factorial(4); // d saņem vērtību 24
const e = factorial(5); // e saņem vērtību 120

Šajā piemērā katrs console.log() izsauks funkciju factorial() ar atbilstošu argumentu, un tā atgriezīs attiecīgo faktoriāļa vērtību.

Ir arī citi veidi, kā izsaukt funkcijas. Bieži gadās, ka funkciju nepieciešams izsaukt dinamiski vai arī funkcijai ir mainīgs argumentu skaits, vai arī nepieciešams iestatīt funkcijas izsaukuma kontekstu uz noteiktu objektu, kas tiek noteikts darbības laikā.

Lai izmantotu metodi call(), jums ir jānorāda objekts, kuram jābūt kontekstā, kad tiek izsaukta funkcija, un jānorāda funkcijas argumenti atsevišķi, ar komatu atdalot. Piemēram:

function greet(greeting) { console.log(greeting + ' ' + this.name); }

let person1 = { name: 'Alise' }; let person2 = { name: 'Bobs' };

greet.call(person1, 'Sveiki'); // "Sveika Alise" greet.call(person2, 'Sveiks'); // "Sveiks Bob"


Metode apply() darbojas līdzīgi, bet tās otrais arguments ir masīvs ar argumentiem, nevis atsevišķi norādīti argumenti.

Piemēram:

greet.apply(person1, ['Sveiki']); // "Sveika Alise" greet.apply(person2, ['Sveiks']); // "Sveiks Bob"

Abas šīs metodes ļauj jums izsaukt funkciju dinamiski, t.i., mainot kontekstu un argumentus laikā, kad tiek izsaukta funkcija. Tāpat tās ļauj jums izsaukt funkcijas ar mainīgu argumentu skaitu.

 

Funkciju “hoisting”

 

Apskatiet zemāk esošo piemēru:

console.log(square(5)); // 25

function square(n) { return n * n; }

Šajā piemērā kods darbojas, lai gan funkcijas deklarācija parādās pēc tās izsaukuma. Tas ir iespējams, jo funkciju deklarācijas tiek “hoisted” (paceltas) uz augšu programmas kodā, tādējādi padarot tās pieejamas visā kodā. Tāpat arī funkciju izsējumu deklarācijas tiek “hoisted”.

Tā kā funkciju deklarācijas tiek “hoisted”, labāka prakse ir deklarēt visus savas programmas funkcijas sākumā, lai izvairītos no nejaušām kļūdām un radītu vēl skaidrāku kodu.

Funkciju “hoisting” darbojas tikai ar funkciju deklarācijām – ne ar funkciju izteiksmēm. Zemāk esošais kods nedarbosies:

console.log(square(5)); // Error

let square = function(n) { return n * n; };

Šajā piemērā mainīgais “square” tiek deklarēts kā funkcijas izteiksme, tādēļ tas netiek “hoisted” un nav pieejams zemāk esošajā izsaukumā. Lai šis kods darbotos, jāpārvieto funkcijas izteiksme uz augšu, lai tā būtu pieejama izsaukumam.

Labāka prakse ir izmantot funkciju deklarācijas, nevis funkciju izteiksmes, jo tās tiek “hoisted” un ir vieglāk lasāmas. Tomēr dažreiz funkciju izteiksmes ir nepieciešamas, piemēram, kad jums ir nepieciešams padot funkciju kā argumentu citai funkcijai vai piešķirt funkciju mainīgajam. Šajās situācijās rūpīgi jāpārvalda koda secība, lai izvairītos no kļūdām.

Funkcijas skoups

Funkcijā definētie mainīgie nevar tikt piekļauti no jebkuras vietas ārpus funkcijas, jo mainīgais ir definēts tikai funkcijas skoupā. Tomēr funkcija var piekļūt visiem mainīgajiem un funkcijām, kas ir definētas tajā skoupā, kurā tā ir definēta.

Citiem vārdiem sakot, funkcija, kas ir definēta globālajā skoupā, var piekļūt visiem mainīgajiem, kas ir definēti globālajā skoupā. Funkcija, kas ir definēta citas funkcijas ietvaros, var arī piekļūt visiem mainīgajiem, kas ir definēti tās vecāka funkcijā, un jebkuriem citiem mainīgajiem, pie kuriem vecāka funkcija var piekļūt.

Piemēram:

// Sekojošie maninīgie ir definēti globālajā skoupā
const num1 = 20;
const num2 = 3;
const name = 'Čmok';

// Šī funkcija definēta globālajā skoupā
function multiply() {
return num1 * num2;
}

multiply(); // Atgriež 60

// ligzdotās funkcijas piemērs
function getScore() {
const num1 = 2;
const num2 = 3;

function add() {
return `${name} scored ${num1 + num2}`;
}

return add();
}

getScore(); // Atgriež "Čmok scored 5"

Šeit būs arī cits piemērs:

let x = 'globāls';

function outer() { let y = 'ārējs';

function inner() { let z = 'iekšējs'; console.log(x); // "globāls" console.log(y); // "ārējs" console.log(z); // "iekšējs" }

iekšējs(); }

ārējs();

console.log(x); // "globāls" console.log(y); // Error: y nav definēts console.log(z); // Error: z nav definēts

Funkcija iekšējs() var piekļūt mainīgajiem x, y un z, jo tā ir definēta tajā pašā skoupā kā mainīgie, bet mainīgie x un y nav pieejami ārpus funkcijas ārējs(), un mainīgais z nav pieejams ārpus funkcijas iekšējs().

Piemēram, apskatiet šādu funkcijas deklarāciju:

const fūūū = function bar() {

// teikumi tiek izpildīti šeit

}

Funkcijas telpā sekojošie ir vienādi:

  • bar()
  • arguments.callee()
  • fūūū()

Šajā piemērā funkcija bar() ir pieejama tikai tās pašas funkcijas telpā, bet, izmantojot mainīgo fūūū, jūs varat izsaukt šo funkciju no jebkuras vietas ārpus tās. Tāpat arī jūs varat izmantot arguments.callee izsaukumu, lai izsauktu pašu funkciju no tās pašas funkcijas telpā.

Arguments.callee ir objekts, kas atsaucas uz pašu izsauktās funkcijas objektu. Tā ir pieejama tikai funkcijas telpā un ļauj jums izsaukt pašu funkciju no tās pašas funkcijas telpā. Tomēr tā nav ieteicama lietošana, jo tā var radīt neskaidrības un var būt nederīga ar jaunākajām JavaScript versijām. Tāpēc labāka prakse ir izmantot explicitu funkcijas nosaukumu, lai izsauktu funkciju no tās pašas telpā.

Funkcija, kas izsauc sevi, saucas par rekursīvu funkciju. No dažādiem viedokļiem rekursija ir līdzīga ciklam. Abi izpilda vienādu kodu vairākas reizes, un abiem ir nepieciešams nosacījums (lai izvairītos no bezgalīga cikla vai, tiešāk sakot, bezgalīgas rekursijas šajā gadījumā).

Piemēram, šeit ir funkcija, kas rekursīvi aprēķina faktoriāļus:

function factorial(n) {

if (n === 1) { return 1;

}

else { return n * factorial(n - 1);

}

}

Kā jūs varētu redzēt, šī funkcija izsauc sevi atkārtoti, līdz sasniedz vērtību 1, un pēc tam sāk atgriezt faktoriāļu vērtības. Piemēram, ja izsauktu funkciju factorial(4), tā atgrieztu vērtību 24 (4 * 3 * 2 * 1).

Tāpat kā cikli, rekursīvās funkcijas ir jāizmanto uzmanīgi, lai izvairītos no bezgalīgas rekursijas. Tāpēc katrā rekursīvā funkcijā ir jābūt nosacījumam, kas nosaka, kad rekursīvā izsaukšana ir jāpārtrauc. Šajā gadījumā tas ir nosacījums:

"if (n === 1)"

, kurš nosaka, ka, ja funkcijai tiek padots arguments ar vērtību 1, tā atgriež vērtību 1 un pārtrauc rekursīvo izsaukšanu.

Rekursīvās funkcijas var būt noderīgas, lai apstrādātu struktūras, kurām ir dziļums vai kurām ir atkārtots šķērslis, piemēram, izvēlnes, kuru izvēles atkarīgas no iepriekšējām izvēlēm, vai failu sistēmas, kurās ir mapes un apakšmapes. Tomēr tās var būt arī ātrākas un vieglāk lasāmas, ja tiek izmantots cikls vai citas programmēšanas struktūras, tāpēc rekursīvās funkcijas ir jāizmanto uzmanīgi un tikai tad, ja tās ir vispiemērotākās struktūras izpildei.

Piemeram, apskati sekojošo ciklu:

let x = 0;
while (x < 10) { // "x < 10" ir cikla nosacījums 
// darbi 
x++; 
}

To var pārvērst par rekursīvas funkcijas deklarāciju, kuru seko izsaukums šai funkcijai:

function loop(x) { // "x >= 10" ir izejas nosacījums (ekvivalents "!(x < 10)")

if (x >= 10) { return; } // darbi

loop(x + 1); // rekursīvais izsaukums

}

loop(0);

Tomēr daži algoritmi nevar būt vienkārši iteratīvi cikli. Piemēram, iegūt visus kokstruktūras mezglus (piemēram, DOM) ir vienkāršāk, izmantojot rekursiju:

function getNodes(node) {

if (node === null) { return []; }

let nodes = [node];

for (let i = 0; i < node.children.length; i++) {

nodes = nodes.concat(getNodes(node.children[i]));

}

return nodes; }

let treeRoot = document.getElementById('tree');

let treeNodes = getNodes(treeRoot);

Šajā piemērā funkcija getNodes() rekursīvi izsauc sevi katram apakšmezglam, lai iegūtu visus mezglus kokā. Tāpat kā citās rekursīvās funkcijās, ir svarīgi nodrošināt izejas nosacījumu, lai izvairītos no bezgalīgas rekursijas. Šajā gadījumā tas ir “node === null”, kas nosaka, ka rekursīvā izsaukšana tiek pārtraukta, kad tiek sasniegts null mezgls (t.i., beidzas koks).

Patiesībā, rekursija pati izmanto kopu: funkciju kopas. Kopu rakstura uzvedību var redzēt sekojošajā piemērā:

function foo(i) {

if (i < 0) {

return;

}

console.log(sākums: ${i});

foo(i - 1);

console.log(beigas: ${i});

}

foo(3);

// Izdrukā:

// sākums: 3

// sākums: 2

// sākums: 1

// sākums: 0

// beigas: 0

// beigas: 1

// beigas: 2

// beigas: 3

Ligzdojošās iekšējās funkcijas (nested) un ietvertās funkcijas (closures)

 

Jūs varat ievietot funkciju citā funkcijā. Iekļautā (iekšējā) funkcija ir privāta tās saturošajai (ārējai) funkcijai.

Tā arī veido closures. Closure ir izteiksme (visbiežāk funkcija), kurā var būt brīvie mainīgie kopā ar vidi, kas saista šos mainīgos (kas “aizver” izteiksmi).

Tā kā iekļautā funkcija ir closure, tas nozīmē, ka iekšējā funkcija var “pārmantot” savas saturošās funkcijas argumentus un mainīgos. Citām vārdiem sakot, iekšējā funkcija satur ārējās funkcijas scope.

Lai to izklāstītu kopsavilkumā:

Iekšējā funkcija var tikt piekļauta tikai no ārējās funkcijas izteiksmēm. Iekšējā funkcija veido closures: iekšējā funkcija var izmantot ārējās funkcijas argumentus un mainīgos, bet ārējā funkcija nevar izmantot iekšējās funkcijas argumentus un mainīgos.

Tātad, closures ir izteiksmes (parasti funkcijas), kurās ir brīvie mainīgie un tie atrodās vidē, kas saista šos mainīgos. Iekšējās funkcijas veido closures, jo tās var izmantot ārējās funkcijas argumentus un mainīgos, bet ārējās funkcijas nevar izmantot iekšējās funkcijas argumentus un mainīgos. Closures ir noderīgas, lai nodrošinātu privātumu mainīgajiem un funkcijām, kā arī, lai nodrošinātu, ka mainīgie un funkcijas ir pieejamas tikai noteiktās vidēs. Tās var arī būt noderīgas, lai izveidotu vērtības, kas atkarīgas no noteiktām vērtībām un funkcijām, un lai nodrošinātu, ka šīs vērtības tiek saglabātas un pieejamas tālākajā programmēšanas procesā.

Sekojošajā piemērā varam apskatīt ligzdojošās funkcijas:

function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
const a = addSquares(2, 3); // atgriež 13
const b = addSquares(3, 4); // atgriež 25
const c = addSquares(4, 5); // atgriež 41

Jo iekšējā funkcija veido closures, jūs varat izsaukt ārējo funkciju un norādīt argumentus gan ārējai, gan iekšējai funkcijai:

function ārpusē(x) {
  function iekšpusē(y) {
    return x + y;
  }


  return iekšpusē;
}
const fnIekšpusē = ārpusē(3); // funkcija, kas pievieno 3 tam, ko tu tai dod
const rezultāts = fnIekšpusē(5); // atgriež 8
const rezultāts1 = ārpusē(3)(5); // atgriež 8

Mainīgo saglabāšana

 

Apsveriet, kā x tiek saglabāts, kad inside tiek atgriezts. Closure ir jāsaglabā argumenti un mainīgos visās vidēs, uz kurām tā norāda. Tā kā katrs izsaukums piedāvā potenciāli atšķirīgus argumentus, katram izsaukumam  ārpusē tiek izveidota jauna closure. Atmiņa var tikt atbrīvota tikai tad, kad atgrieztais inside vairs nav pieejams.

Tas nav atšķirīgs no citu objektu atsauču saglabāšanas, bet parasti ir mazāk acīmredzams, jo atsauces nav tieši iestatītas un nevar tās pārbaudīt.

Daudzkārt iegultas funkcijas

Funkcijas var būt daudzkārt iegultas. Piemēram:

Funkcija (A) satur funkciju (B), kas pašā sevī satur funkciju (C). Gan funkcijas B un C veido closures šeit. Tātad B var piekļūt A, un C var piekļūt B. Papildus tam, jo C var piekļūt B, kas var piekļūt A, C var arī piekļūt A. Tātad closures var saturēt vairākas vides; tie rekursīvi satur tās funkcijas vidi, kurās tās atrodas. Tas saucas par scope chaining. (Iemesls, kāpēc tas saucas “chaining”, tiks skaidrots vēlāk.)

Lai to vieglāk izprastu, apskatīsim sekojošu piemēru:

function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // Izlogo rezultātu 6 (kas ir tas pats kas 1 + 2 + 3)

Šajā piemērā C piekļūst B y un A x.

Tas var tikt izdarīts, jo:

B veido closure, kas ietver A (t.i., B var piekļūt A argumentiem un mainīgajiem). C veido closure, kas ietver B. Jo C closure ietver B un B closure ietver A, tad C closure arī ietver A. Tas nozīmē, ka C var piekļūt gan B, gan A argumentiem un mainīgajiem. Citām vārdiem sakot, C savieno B un A vidus, šajā secībā. Tomēr atpakaļējā secībā tas nav patiess. A nevar piekļūt C, jo A nevar piekļūt jebkuram B argumentam vai mainīgajam, kurš ir C mainīgais. Tādējādi C paliek privāts tikai B.

Vārdu konflikti

Ja divi argumenti vai mainīgie vienā closure vidē ir ar vienādiem vārdiem, rodas vārda konflikts! Vairāk iegultas vides tiek ņemtas vērā pirms ārējām. Tādēļ iekšējā vidē tiek piešķirta visaugstākā prioritāte, bet ārējā vidē tiek piešķirta viszemākā prioritāte. Tas ir scope chain. Pirmais ķēdē ir iekšējā vidē, bet pēdējais ir ārējā vidē.

Aplūkosim šādu piemēru:

function ārpuse() {
    const x = 5;
    function iekšpuse(x) {
      return x * 2;
    }
    return iekšpuse;
  }
 
  ārpuse()(10); // atgriež 20 nevis 10

Šajā piemērā ārējā funkcija ārpuse() definē mainīgo x ar vērtību 5 un iekšējo funkciju iekšpuse(), kas pieņem vienu argumentu x. Iekšējā funkcija iekšpuse() atgriež argumenta x reizinājumu ar 2. Kad tiek izsaukta ārējā funkcija ārpuse() ar argumentu 10, tiek izsaukta iekšējā funkcija iekšpuse() ar argumentu 10.

Tomēr, jo x argumentam iekšējā funkcijā ir vienāds nosaukums kā mainīgajam x ārējā funkcijā, tiek radīts vārda konflikts. Šajā gadījumā iekšējās vides (iekšējās funkcijas) x tiek ņemts vērā pirms ārējās vides (ārējās funkcijas) x, tādēļ tiek atgriezta vērtība 20, nevis 10.

Ietvērumi – ietvari, jeb closures

Closures ir viena no spēcīgākajām JavaScript iespējām. JavaScript atļauj funkciju iegulšanu un piešķir iekšējai funkcijai pilnu pieeju visiem mainīgajiem un funkcijām, kas definētas ārējā funkcijā (un visiem citiem mainīgajiem un funkcijām, pie kurām ārējā funkcija var piekļūt).

Tomēr ārējā funkcija nedrīkst piekļūt mainīgajiem un funkcijām, kas definētas iekšējā funkcijā. Tā nodrošina kādu veidu iekapsulēšanu iekšējās funkcijas mainīgajiem.

Papildus tam, jo iekšējā funkcija var piekļūt ārējās funkcijas vidē, ārējās funkcijas definētie mainīgie un funkcijas dzīvos ilgāk par ārējās funkcijas izpildes laiku, ja iekšējā funkcija spēj izdzīvot pāri ārējās funkcijas dzīves laikam. Closure tiek izveidota, ja iekšējā funkcija kādā veidā tiek padarīta pieejama jebkurā vidē ārpus ārējās funkcijas.

Lai izprastu closures, svarīgi ir izprast, kā tiek piešķirtas vērtības mainīgajiem un kā tiek radīti mainīgo vārdi. Ja mainīgais tiek definēts ar var vai let, tā vērtība tiek piešķirta tikai tad, kad tā tiek izpildīta. Ja mainīgais tiek definēts ar const, tā vērtība tiek piešķirta tikai tad, kad tā tiek deklarēta.

Tomēr, ja mainīgais tiek definēts bez neviena no šiem atslēgvārdiem (piemēram, x = 5), tā vērtība tiek piešķirta tikai tad, kad tā tiek piešķirta.

Arī svarīgi ir saprast, ka mainīgo vārdi tiek radīti tikai tad, kad tos definē, un tā vērtības tiek piešķirtas tikai tad, kad tās tiek piešķirtas. Tādēļ, ja mainīgais tiek definēts ar var vai let, tā vārds tiek radīts un piešķirts tikai tad, kad tā tiek izpildīta, bet ja mainīgais tiek definēts ar const, tā vārds tiek radīts un piešķirts tikai tad, kad tā tiek deklarēta.

const pet = function (name) {   // Ārējā funkcija definē mainīgo "name"
    const getName = function () {
      // iekšējai funkcijai ir pieeja pie "name" mainīgā no ārējās funkcijas
      return name;
    }
    return getName; // atgriež iekšējās funkcijas
  }
  const myPet = pet('Vivie');
 
  myPet(); // atgriež "Vivie"
Izpildot kodu var būt daudz sarežģītāk par iepriekš minēto. Var tikt atgriezts objekts, kurš satur metodes, lai manipulētu ar ārējās funkcijas iekšējiem mainīgajiem.
const createPet = function (name) {
    let sex;
 
    const pet = {
      // setName(newName) ir ekvavilents setName: function (newName)
      // šajā saturā
      setName(newName) {
        name = newName;
      },
 
      getName() {
        return name;
      },
 
      getSex() {
        return sex;
      },
 
      setSex(newSex) {
        if (typeof newSex === 'string' &&
          (newSex.toLowerCase() === 'male' || newSex.toLowerCase() === 'female')) {
          sex = newSex;
        }
      }
    };
 
    return pet;
  }
 
  const pet = createPet('Vivie');
  pet.getName();                  // Vivie
 
  pet.setName('Oliver');
  pet.setSex('male');
  pet.getSex();                   // male
  pet.getName();                  // Oliver
Iepriekš minētajā kodā ārējās funkcijas mainīgais “name” ir pieejams iekšējām funkcijām, un nav citu veidu, kā piekļūt iekšējiem mainīgajiem, izņemot caur iekšējām funkcijām. Iekšējie mainīgie iekšējās funkcijās darbojas kā droši glabātuves ārējiem argumentiem un mainīgajiem. Tie uztur “persistent” un “encapsulated” datus, ar kuriem var strādāt iekšējās funkcijas. Funkcijas pat nav jāpiesaista mainīgajam vai jānodod tām vārds.
const getCode = (function () {
const apiCode = '0]Eal(eh&2'; // A code we do not want outsiders to be able to modify…

return function () {
return apiCode;
};
})();

getCode(); // Returns the apiCode

 

Iepriekš minētajā kodā tiek izmantota anonīma autoejecutable funkcija, lai radītu jaunu apgabalu un piešķirtu tai vērtību mainīgajam “apiCode”. Tālāk tiek atgriezta iekšējā funkcija, kas piekļūst “apiCode” mainīgajam. Rezultātā tiek izveidota “closure”, kurā “apiCode” mainīgais ir pieejams tikai iekšējai funkcijai un nav pieejams ārējam apgabalam. Tādējādi tiek nodrošināta privātība un aizsargāts “apiCode”.

Iekšējā funkcija var tikt izsaukta, izmantojot “getCode()”, un tā atgriezīs “apiCode” vērtību, bet neļaus ārējam apgabalam to modificēt vai redzēt.

Izmantojot funkciju argumentus

Izmantojot arguments objektu Funkcijas argumenti tiek uzturēti masīvam līdzīgā objektā. Funkcijā varat piekļūt tai padotajiem argumentiem, izmantojot šādus veidus:

arguments[i]

kur ” i “ ir argumenta kārtas numurs, sākot no 0. Tātad, pirmais arguments, kas tiek padots funkcijai, būtu arguments[0]. Arguments kopējais skaits ir norādīts ar arguments.length.

Izmantojot arguments objektu, varat izsaukt funkciju ar vairāk argumentiem, nekā tā formāli pieņem. Tā ir bieži noderīga, ja nezināt iepriekš, cik argumentus tiks padoti funkcijai. Varat izmantot arguments.length, lai noteiktu argumentu skaitu, ko patiesībā padod funkcijai, un pēc tam piekļūt katram argumentam, izmantojot arguments objektu.

Piemeram, apskatiet funkciju, kas apvieno vairākas virknes. Funkcijas formālais arguments ir virkne, kas norāda simbolus, kas atdala apvienojamos vienumus. Funkcija ir definēta šādi:

function myConcat(separator) {
let result = ''; // inicializē sarakstu
// iterē caur argumentiem
for (let i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
Šai funkcijai varat padot jebkuru argumentu skaitu, un tā apvieno katru argumentu vienā virknē “sarakstā”:
// returns "red, orange, blue, "
myConcat(', ', 'red', 'orange', 'blue');

// returns "elephant; giraffe; lion; cheetah; "
myConcat('; ', 'elephant', 'giraffe', 'lion', 'cheetah');

// returns "sage. basil. oregano. pepper. parsley. "
myConcat('. ', 'sage', 'basil', 'oregano', 'pepper', 'parsley');

Funkciju parametri

 

Ir divi īpaši parametru sintakses veidi: noklusējuma parametri un pārējie parametri.

Noklusējuma parametri JavaScript valodā funkcijas parametri noklusējumā ir undefined. Tomēr dažās situācijās var būt noderīgi iestatīt citu noklusējuma vērtību. Tieši tā dara noklusējuma parametri.

Pagātnē par noklusējuma vērtību iestatīšanu bieži izmantoja stratēģiju, kurā tika testētas parametru vērtības funkcijas telpā un piešķirta vērtība, ja tie ir undefined.

Sekojošajā piemērā, ja nav sniegta vērtība b, tā vērtība būtu undefined, vērtējot a * b, un parasti multiply izsaukšana būtu atgriezusi NaN. Tomēr šo novērš otrā rindiņa šajā piemērā:

function multiply(a, b) {
b = typeof b !== 'undefined' ? b : 1;
return a * b;
}

multiply(5); // 5

Izmantojot noklusējuma parametrus, vairs nav nepieciešama manuāla pārbaude funkcijas telpā. Varat iestatīt 1 kā noklusējuma vērtību b funkcijas galvā:

function multiply(a, b = 1) {
return a * b;
}

multiply(5); // 5

Pārējie parametri

Pārējie parametri Pārējo parametru sintakse mums ļauj pārstāvēt nezināmu argumentu skaitu kā masīvu.

Sekojošajā piemērā funkcija multiply izmanto pārējos parametrus, lai savāktu argumentus no otrā līdz beigām. Pēc tam funkcija reizina šos ar pirmo argumentu.

function multiply(multiplier, ...theArgs) {
return theArgs.map((x) => multiplier * x);
}

const arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]

Arrow funkcijas, jeb Bultiņu funkcijas

Bultiņu funkciju izteiksme (arī saukta par tauku bultiņu,(fat arrow) lai atšķirtu no hipotētiskās -> sintakses nākotnes JavaScript) ir īsāka sintakse salīdzinājumā ar funkciju izteiksmēm un tai nav savs arguments, “super” vai “new.target.” Bultiņu funkcijas vienmēr ir anonīmas.

Divi faktori ietekmēja bultiņu funkciju ieviešanu: īsākas funkcijas un this nesaistība.

Salīdzinājums:

const a = [
'Hydrogen',
'Helium',
'Lithium',
'Beryllium'
];

const a2 = a.map(function(s) { return s.length; });

console.log(a2); // [8, 6, 7, 9]

const a3 = a.map((s) => s.length);

console.log(a3); // [8, 6, 7, 9]

Turpinājums sekos…

Loading

Noderīgs raksts? Dalies ar citiem: