Paprastas vadovas, padėsiantis suprasti „JavaScript“ uždarymą

„JavaScript“ uždarymas yra viena iš tų sąvokų, kurioms daug kas stengiasi sukti galvą. Kitame straipsnyje aiškiai paaiškinsiu, kas yra uždarymas, ir parvesiu tašką namo naudodamas paprastus kodų pavyzdžius.

Kas yra uždarymas?

Uždarymas yra „JavaScript“ funkcija, kai vidinė funkcija turi prieigą prie išorinių (uždengiančių) funkcijos kintamųjų - taikymo srities grandinės.

Uždarymas turi tris taikymo sritis:

  • ji turi prieigą prie savo apimties - kintamieji, apibrėžti tarp jo garbanotųjų skliaustų
  • ji turi prieigą prie išorinių funkcijos kintamųjų
  • ji turi prieigą prie globalių kintamųjų

Neišmanantiems šis apibrėžimas gali atrodyti tik kaip daugybė žargono!

Bet kas iš tikrųjų yra uždarymas?

Paprastas uždarymas

Pažvelkime į paprastą uždarymo pavyzdį „JavaScript“:

funkcija išorinė () {
   var b = 10;
   funkcija vidinė () {
        
         var a = 20;
         console.log (a + b);
    }
   grįžti vidinis;
}

Čia turime dvi funkcijas:

  • išorinė išorinė funkcija, kurios kintamasis b ir grąžina vidinę funkciją
  • vidinė funkcijos vidinė, kurios kintamasis vadinamas a ir prie jo išorinio kintamojo b patenka savo funkcijos kūne

Kintamojo b apimtis yra ribojama išorinės funkcijos, o kintamojo apimtis yra ribojama vidinės funkcijos.

Dabar iškvieskime išorinę () funkciją ir išsaugokime išorinės () funkcijos rezultatą kintamajame X. Tada antrą kartą iškvieskime išorinę () funkciją ir saugokime ją kintamajame Y.

funkcija išorinė () {
   var b = 10;
   funkcija vidinė () {
        
         var a = 20;
         console.log (a + b);
    }
   grįžti vidinis;
}
var X = išorinis (); // external () iškviečiamas pirmą kartą
var Y = išorinis (); // external () iškvietė antrą kartą

Pažiūrėkime žingsnis po žingsnio, kas nutinka, kai pirmą kartą įjungiama išorinė () funkcija:

  1. Sukurtas kintamasis b, jo apimtis ribojama išorine () funkcija, o jo vertė yra 10.
  2. Kita eilutė yra funkcijos deklaracija, taigi nieko nereikia vykdyti.
  3. Paskutinėje eilutėje grįžtamasis vidinis ieško kintamojo, vadinamo vidiniu, nustato, kad šis kintamasis vidinis iš tikrųjų yra funkcija, ir taip grąžina visą vidinį funkcijos kūną.
    [Atminkite, kad grįžimo sakinys nevykdo vidinės funkcijos - funkcija vykdoma tik tada, kai seka () -, o grąžinimo sakinys grąžina visą funkcijos pagrindą.]
  4. Grąžinimo deklaracijos turinys saugomas X.
    Taigi X kaups:
     funkcija vidinė () {
     var a = 20;
    console.log (a + b);
    }
  5. Funkcija išorinė () užbaigia vykdymą, o visi kintamieji, esantys išorės () srityje, nebeegzistuoja.

Šią paskutinę dalį svarbu suprasti. Kai funkcija baigs vykdyti, visi kintamieji, kurie buvo apibrėžti funkcijos apimtyje, nustoja egzistuoti.

Funkcijos viduje apibrėžto kintamojo gyvenimo trukmė yra funkcijos vykdymo trukmė.

Tai reiškia, kad console.log (a + b) kintamasis b egzistuoja tik vykdant išorinę () funkciją. Kai išorinė funkcija baigiama vykdyti, kintamojo b nebėra.

Kai funkcija vykdoma antrą kartą, funkcijos kintamieji vėl sukuriami ir gyvena tik tol, kol funkcija baigs vykdyti.

Taigi, kai išorinis () yra įjungiamas antrą kartą:

  1. Sukuriamas naujas kintamasis b, jo apimtis apsiriboja išorine () funkcija, o jo vertė yra 10.
  2. Kita eilutė yra funkcijos deklaracija, taigi nieko nereikia vykdyti.
  3. grįžti vidinis grąžina visą vidinį funkcijos kūną.
  4. Grąžinimo deklaracijos turinys saugomas Y.
  5. Funkcija išorinė () užbaigia vykdymą, o visi kintamieji, esantys išorės () srityje, nebeegzistuoja.

Svarbus dalykas yra tai, kad antrą kartą iškviečiant išorinę () funkciją, kintamasis b sukuriamas iš naujo. Be to, kai išorinė () funkcija baigia vykdyti antrą kartą, šis naujas kintamasis b vėl nustoja egzistuoti.

Tai yra svarbiausias dalykas, kurį reikia suvokti. Funkcijų kintamieji atsiranda tik tada, kai funkcija veikia, ir nustoja egzistuoti, kai funkcijos baigiamos vykdyti.

Grįžkime prie kodo pavyzdžio ir pažvelkime į X ir Y. Kadangi išorinė () funkcija vykdant grąžina funkciją, kintamieji X ir Y yra funkcijos.

Tai galima lengvai patikrinti pridedant šiuos „JavaScript“ kodus:

console.log (typeof (X)); // X yra funkcijos funkcija
console.log (typeof (Y)); // Y yra tipo funkcijos

Kadangi kintamieji X ir Y yra funkcijos, mes galime juos vykdyti. „JavaScript“ programoje funkciją galima vykdyti pridedant () po funkcijos pavadinimo, pvz., X () ir Y ().

funkcija išorinė () {
var b = 10;
   funkcija vidinė () {
        
         var a = 20;
         console.log (a + b);
    }
   grįžti vidinis;
}
var X = išorinis ();
var Y = išorinis ();
// išorinių () funkcijų vykdymo pabaiga
X (); // X () iškviečiamas pirmą kartą
X (); // X () iškvietė antrą kartą
X (); // X () iškvietė trečią kartą
Y (); // Y () iškviečiamas pirmą kartą

Kai vykdome X () ir Y (), mes iš esmės vykdome vidinę funkciją.

Pažiūrėkime žingsnis po žingsnio, kas nutinka, kai X () vykdomas pirmą kartą:

  1. Sukuriamas kintamasis a, o jo vertė yra 20.
  2. Dabar „JavaScript“ bando vykdyti + b. Čia viskas pasidaro įdomu. „JavaScript“ žino, kad egzistuoja, nes tik sukūrė. Tačiau kintamojo b nebėra. Kadangi b yra išorinės funkcijos dalis, b egzistuotų tik tada, kai vykdoma išorinė () funkcija. Kadangi išorinė () funkcija baigė vykdyti daug anksčiau nei mes sukvietėme X (), visi išorinės funkcijos apimties kintamieji nustoja egzistuoti, taigi kintamasis b nebeegzistuoja.

Kaip „JavaScript“ tai tvarko?

Uždarymai

Vidinė funkcija gali pasiekti uždaromosios funkcijos kintamuosius dėl „JavaScript“ uždarymų. Kitaip tariant, vidinė funkcija išsaugo uždarosios funkcijos apimties grandinę tuo metu, kai buvo vykdoma uždengimo funkcija, taigi ji gali pasiekti uždaromosios funkcijos kintamuosius.

Mūsų pavyzdyje vykdant išorinę () funkciją vidinė funkcija išsaugojo b = 10 reikšmę ir toliau ją išsaugojo (uždarė).

Dabar jis nurodo savo apimties grandinę ir pastebi, kad jos apimties grandinėje yra kintamojo b vertė, nes ji uždarė b reikšmę uždarymo metu, kai buvo atlikta išorinė funkcija.

Taigi, „JavaScript“ žino a = 20 ir b = 10 ir gali apskaičiuoti a + b.

Tai galite patikrinti pridėdami šią kodo eilutę prie aukščiau pateikto pavyzdžio:

funkcija išorinė () {
var b = 10;
   funkcija vidinė () {
        
         var a = 20;
         console.log (a + b);
    }
   grįžti vidinis;
}
var X = išorinis ();
console.dir (X); // naudokite console.dir (), o ne console.log ()

„Google Chrome“ atidarykite elementą „Tikrinimas“ ir eikite į konsolę. Elementą galite išplėsti, kad iš tikrųjų matytumėte uždarymo elementą (parodyta trečioje paskutinėje eilutėje žemiau). Atkreipkite dėmesį, kad uždaryme b = 10 reikšmė išsaugoma net ir tada, kai išorinė () funkcija užbaigia jos vykdymą.

Kintamasis b = 10 yra išsaugotas „Closure“, „Closures“ „Javascript“

Pažvelkime į uždarymo apibrėžimą, kurį matėme pradžioje, ir pažiūrėkime, ar jis dabar turi daugiau prasmės.

Taigi vidinė funkcija turi tris taikymo sritis:

  • prieiga prie savo apimties - kintamasis a
  • prieiga prie išorinių funkcijos kintamųjų - kintamasis b, kurį ji uždarė
  • prieiga prie bet kokių globalių kintamųjų, kurie gali būti apibrėžti

Uždaros akcijos

Norėdami važiuoti namo uždarymo tašku, papildykime pavyzdį pridėdami tris kodo eilutes:

funkcija išorinė () {
var b = 10;
var c = 100;
   funkcija vidinė () {
        
         var a = 20;
         console.log ("a =" + a + "b =" + b);
         a ++;
         b ++;
    }
   grįžti vidinis;
}
var X = išorinis (); // external () iškviečiamas pirmą kartą
var Y = išorinis (); // external () iškvietė antrą kartą
// išorinių () funkcijų vykdymo pabaiga
X (); // X () iškviečiamas pirmą kartą
X (); // X () iškvietė antrą kartą
X (); // X () iškvietė trečią kartą
Y (); // Y () iškviečiamas pirmą kartą

Paleidę šį kodą, console.log pamatysite šią išvestį:

a = 20 b = 10
a = 20 b = 11
a = 20 b = 12
a = 20 b = 10

Išnagrinėsime šį kodą žingsnis po žingsnio, kad sužinotume, kas tiksliai vyksta, ir pamatykite, kaip viskas veikia!

var X = išorinis (); // external () iškviečiamas pirmą kartą

Pirmą kartą įjungiama išorinė () funkcija. Atliekami šie veiksmai:

  1. Sukuriamas kintamasis b ir yra nustatytas 10
    Sukuriamas kintamasis c ir nustatytas 100
    Pavadinkime tai b (pirmąjį kartą) ir c (pirmąjį kartą) mūsų pačių nurodymui.
  2. Vidinė funkcija grąžinama ir priskiriama X
    Šiuo metu kintamasis b yra uždaromas vidinėje funkcijos apimties grandinėje kaip užsegimas, kurio b = 10, nes vidinis naudoja kintamąjį b.
  3. Išorinė funkcija užbaigia vykdymą, o visi jos kintamieji nustoja egzistuoti. Kintamasis c nebeegzistuoja, nors kintamasis b egzistuoja kaip uždarumas vidiniame.
var Y = išorinis (); // external () iškvietė antrą kartą
  1. Kintamasis b sukuriamas iš naujo ir yra nustatytas 10
    Kintamasis c sukuriamas iš naujo.
    Atminkite, kad nors išorinis () buvo vykdomas vieną kartą, kol nustojo egzistuoti kintamieji b ir c, kai funkcija buvo atlikta, jie bus sukurti kaip visiškai nauji kintamieji.
    Pavadinkime šiuos b (antrą kartą) ir c (antrą laiką), kad galėtume patys įvertinti.
  2. Vidinė funkcija grąžinama ir priskiriama Y
    Šiuo metu kintamasis b yra uždaromas vidinėje funkcijos apimties grandinėje kaip užsegimas, kurio b (antrą kartą) = 10, nes vidinis naudoja kintamąjį b.
  3. Išorinė funkcija užbaigia vykdymą, o visi jos kintamieji nustoja egzistuoti.
    Kintamasis c (second_time) nebeegzistuoja, nors kintamasis b (second_time) egzistuoja kaip uždarymas vidiniame.

Dabar pažiūrėkime, kas nutinka, kai vykdomos šios kodo eilutės:

X (); // X () iškviečiamas pirmą kartą
X (); // X () iškvietė antrą kartą
X (); // X () iškvietė trečią kartą
Y (); // Y () iškviečiamas pirmą kartą

Kai pirmą kartą iškviečiamas X (),

  1. Sukuriamas kintamasis a ir nustatomas į 20
  2. reikšmė a = 20, b vertė yra nuo uždarymo vertės. b (pirmas laikas), taigi b = 10
  3. kintamieji a ir b padidinami 1
  4. X () užbaigia vykdymą ir visi jo vidiniai kintamieji - kintamasis a - nustoja egzistuoti.
    Tačiau b (first_time) išliko kaip uždarymas, taigi b (first_time) ir toliau egzistuoja.

Kai X () iškviečiamas antrą kartą,

  1. kintamasis a sukuriamas iš naujo ir nustatomas į 20
     Bet kuri ankstesnė kintamojo a reikšmė nebeegzistuoja, nes ji nustojo egzistuoti, kai X () pirmą kartą užbaigė vykdymą.
  2. reikšmė a = 20
    b reikšmė imama iš uždarymo vertės - b (pirmą kartą)
    Taip pat atkreipkite dėmesį, kad mes padidinome b reikšmę 1 nuo ankstesnio vykdymo, taigi b = 11
  3. kintamieji a ir b dar kartą padidinami 1
  4. X () užbaigia vykdymą ir visi jo vidiniai kintamieji - kintamasis a - nustoja egzistuoti
    Tačiau b (pirmas laikas) išsaugomas, nes uždarymas ir toliau egzistuoja.

Kai X () iškviečiamas trečią kartą,

  1. kintamasis a sukuriamas iš naujo ir nustatomas į 20
    Bet kuri ankstesnė kintamojo a reikšmė nebeegzistuoja, nes ji nustojo egzistuoti, kai X () pirmą kartą užbaigė vykdymą.
  2. reikšmė a = 20, b vertė yra nuo uždarymo vertės - b (pirmą kartą)
    Taip pat atkreipkite dėmesį, kad ankstesniame vykdyme mes padidinome b reikšmę 1, taigi b = 12
  3. kintamieji a ir b dar kartą padidinami 1
  4. X () užbaigia vykdymą, o visi jo vidiniai kintamieji - kintamasis a - nustoja egzistuoti
    Tačiau b (pirmas laikas) išsaugomas, nes uždarymas ir toliau egzistuoja

Kai pirmą kartą šaukiama Y (),

  1. kintamasis a sukuriamas iš naujo ir nustatomas į 20
  2. a = 20 reikšmė, b reikšmė yra nuo uždarymo vertės - b (antrą kartą), taigi b = 10
  3. kintamieji a ir b padidinami 1
  4. Y () užbaigia vykdymą, o visi jo vidiniai kintamieji - kintamasis a - nustoja egzistuoti
    Tačiau b (antras laikas) buvo išsaugotas kaip uždarymas, taigi b (antrasis laikas) ir toliau egzistuoja.

Baigiamosios pastabos

Uždarymas yra viena iš tų subtilių „JavaScript“ sąvokų, kurią iš pradžių sunku suvokti. Bet kai juos supranti, supranti, kad viskas negalėjo būti kitaip.

Tikimės, kad šie žingsnis po žingsnio paaiškinimai padėjo jums suprasti „JavaScript“ uždarymo sąvoką!

Kiti straipsniai:

  • Trumpas „savarankiško pasitelkimo“ funkcijų vadovas „Javascript“
  • Funkcijos „Java“ scenarijaus ir bloko taikymo srities supratimas
  • Kaip naudotis pažadais „JavaScript“
  • Kaip sukurti paprastą „Sprite“ animaciją „JavaScript“