Просто ръководство, което ще ви помогне да разберете затварянията в JavaScript

Затварянията в JavaScript са една от онези концепции, които мнозина се борят да си вървят главата. В следващата статия ще обясня по ясен начин какво е затваряне и ще насоча точката до дома с помощта на прости примери за код.

Какво е затваряне?

Затварянето е функция в JavaScript, при която вътрешна функция има достъп до променливите на външната (ограждаща) функция - верига на обхвата.

Затварянето има три приложни вериги:

  • той има достъп до своя собствен обхват - променливи, определени между неговите къдрави скоби
  • той има достъп до променливите на външната функция
  • той има достъп до глобалните променливи

За непосветените това определение може да изглежда като просто много жаргон!

Но какво всъщност е закриване?

Просто затваряне

Нека разгледаме прост пример за затваряне в JavaScript:

функция външна () {
   var b = 10;
   функция Internal () {
        
         var a = 20;
         console.log (А + В);
    }
   връщане вътрешно;
}

Тук имаме две функции:

  • външна функция, външна, която има променлива b, и връща вътрешната функция
  • вътрешна функция вътрешна, която има своята променлива, наречена a, и има достъп до външна променлива b, в рамките на нейното функционално тяло

Обхватът на променлива b е ограничен до външната функция, а обхватът на променлива a е ограничен до вътрешната функция.

Нека сега се позовем на функцията за външен () и съхраняваме резултата от функцията за външен () в променлива X. След това да извикаме втори път функцията external () и да я съхраним в променлива Y.

функция външна () {
   var b = 10;
   функция Internal () {
        
         var a = 20;
         console.log (А + В);
    }
   връщане вътрешно;
}
var X = външен (); // external () се позова на първия път
var Y = външен (); // external () се позова втори път

Нека да видим стъпка по стъпка какво се случва при първото извикване на външната () функция:

  1. Създава се променлива b, нейният обхват е ограничен до функцията external (), а нейната стойност е 10.
  2. Следващият ред е функционална декларация, така че нищо да не се изпълнява.
  3. На последния ред, връщане вътрешно търси променлива, наречена вътрешна, намира, че тази променлива вътрешна е всъщност функция и така връща цялото тяло на функцията вътрешно.
    [Обърнете внимание, че операторът за връщане не изпълнява вътрешната функция - функция се изпълнява само когато е последвана от () -, а по-скоро отчета за връщане връща цялото тяло на функцията.]
  4. Съдържанието, върнато от оператора за връщане, се съхранява в X.
    По този начин X ще съхранява следното:
     функция Internal () {
     var a = 20;
    console.log (А + В);
    }
  5. Функцията external () завършва изпълнението и всички променливи в обхвата на external () вече не съществуват.

Тази последна част е важна за разбиране. След като дадена функция завърши изпълнението си, всички променливи, дефинирани в рамките на обхвата на функцията, престават да съществуват.

Продължителността на променлива, определена във функцията, е продължителността на изпълнение на функцията.

Това означава, че в console.log (a + b) променливата b съществува само по време на изпълнение на функцията за външен (). След като външната функция приключи изпълнението, променливата b вече не съществува.

Когато функцията се изпълни втори път, променливите на функцията се създават отново и живеят само докато функцията завърши изпълнението.

По този начин, когато вторият () се извиква втори път:

  1. Създава се нова променлива b, нейният обхват е ограничен до функцията external (), а нейната стойност е 10.
  2. Следващият ред е функционална декларация, така че нищо да не се изпълнява.
  3. return Internal връща цялото тяло на функцията вътрешно.
  4. Съдържанието, върнато от декларацията за връщане, се съхранява в Y.
  5. Функцията external () завършва изпълнението и всички променливи в обхвата на external () вече не съществуват.

Важният момент тук е, че когато функцията external () се извика втори път, променливата b се създава наново. Също така, когато външната () функция завърши изпълнението втори път, тази нова променлива b отново престава да съществува.

Това е най-важният момент за осъзнаване. Променливите вътре във функциите съществуват само когато функцията се изпълнява и престават да съществуват, след като функциите завършат изпълнението.

Сега да се върнем към нашия пример с код и да разгледаме X и Y. Тъй като външната () функция при изпълнение връща функция, променливите X и Y са функции.

Това може да бъде лесно потвърдено, като добавите следното към JavaScript кода:

console.log (typeof (X)); // X е от тип функция
console.log (typeof (Y)); // Y е от тип функция

Тъй като променливите X и Y са функции, можем да ги изпълним. В JavaScript функция може да се изпълни чрез добавяне на () след името на функцията, като X () и Y ().

функция външна () {
var b = 10;
   функция Internal () {
        
         var a = 20;
         console.log (А + В);
    }
   връщане вътрешно;
}
var X = външен ();
var Y = външен ();
// край на външни () функции изпълнения
Х(); // X () извика първия път
Х(); // X () се позова втори път
Х(); // X () извика трети път
Y (); // Y () се позова на първия път

Когато изпълняваме X () и Y (), ние по същество изпълняваме вътрешната функция.

Нека разгледаме стъпка по стъпка какво се случва, когато X () се изпълни за първи път:

  1. Създава се променлива a и нейната стойност е 20.
  2. JavaScript сега се опитва да изпълни + b. Ето къде нещата стават интересни. JavaScript знае, че съществува, тъй като току-що го е създал. Променлива b обаче вече не съществува. Тъй като b е част от външната функция, b би съществувала само докато функцията external () е в изпълнение. Тъй като външната () функция завърши изпълнението много преди да се позовем на X (), всички променливи в обхвата на външната функция престават да съществуват и следователно променлива b вече не съществува.

Как JavaScript се справя с това?

Приключване

Вътрешната функция има достъп до променливите на функцията за затваряне поради затваряния в JavaScript. С други думи, вътрешната функция запазва веригата на обхвата на функцията за затваряне по времето, когато функцията за затваряне се изпълнява, и по този начин може да получи достъп до променливите на функцията за затваряне.

В нашия пример вътрешната функция беше запазила стойността на b = 10, когато функцията external () беше изпълнена, и продължи да я запазва (затваря).

Сега той се отнася до неговата обхватна верига и забелязва, че тя има стойността на променлива b в обхвата си верига, тъй като е затворила стойността на b в затваряне в момента, в който е изпълнена външната функция.

По този начин JavaScript знае a = 20 и b = 10 и може да изчисли a + b.

Можете да потвърдите това, като добавите следния ред код към горния пример:

функция външна () {
var b = 10;
   функция Internal () {
        
         var a = 20;
         console.log (А + В);
    }
   връщане вътрешно;
}
var X = външен ();
console.dir (X); // използвайте console.dir () вместо console.log ()

Отворете елемента за проверка в Google Chrome и отидете на конзолата. Можете да разширите елемента, за да видите елемента затваряне (показан в третия до последния ред по-долу). Забележете, че стойността на b = 10 се запазва в затварянето, дори след като външната () функция завърши изпълнението му.

Променлива b = 10 се запазва в затварянето, затварянията в Javascript

Нека сега да преразгледаме дефиницията на закриването, която видяхме в началото и да видим дали сега има повече смисъл.

Така че вътрешната функция има три обхвати:

  • достъп до своя обхват - променлива a
  • достъп до променливите на външната функция - променлива b, която тя е прикрепила
  • достъп до всички глобални променливи, които могат да бъдат определени

Затваряния в действие

За да стигнем до началната точка на затварянето, нека допълним примера, като добавим три реда код:

функция външна () {
var b = 10;
var c = 100;
   функция Internal () {
        
         var a = 20;
         console.log ("a =" + a + "b =" + b);
         с ++;
         б ++;
    }
   връщане вътрешно;
}
var X = външен (); // external () се позова на първия път
var Y = външен (); // external () се позова втори път
// край на външни () функции изпълнения
Х(); // X () извика първия път
Х(); // X () се позова втори път
Х(); // X () извика трети път
Y (); // Y () се позова на първия път

Когато стартирате този код, в console.log ще видите следния изход:

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

Нека разгледаме този код стъпка по стъпка, за да видим какво точно се случва и да видим затваряния в действие!

var X = външен (); // external () се позова на първия път

Функцията external () се извиква от първия път. Следват стъпки:

  1. Променлива b е създадена и е зададена на 10
    Променлива c се създава и се задава на 100
    Нека се обадим на това b (first_time) и c (first_time) за наша собствена справка.
  2. Вътрешната функция се връща и се възлага на X
    В този момент променливата b е затворена във веригата на вътрешната функционална верига като затваряне с b = 10, тъй като вътрешната използва променливата b.
  3. Външната функция завършва изпълнението и всички нейни променливи престават да съществуват. Променливата c вече не съществува, въпреки че променливата b съществува като затваряне във вътрешния.
var Y = външен (); // external () се позова втори път
  1. Променлива b се създава отново и се задава на 10
    Променлива c се създава наново.
    Обърнете внимание, че въпреки че external () е изпълнен веднъж преди да променят b и c, да престанат да съществуват, след като функцията завърши изпълнението, те се създават като чисто нови променливи.
    Нека ги наречем b (second_time) и c (second_time) за собствена справка.
  2. Вътрешната функция се връща и се възлага на Y
    В този момент променливата b е затворена във вътрешната верига на обхвата на функцията като затваряне с b (second_time) = 10, тъй като вътрешната използва променливата b.
  3. Външната функция завършва изпълнението и всички нейни променливи престават да съществуват.
    Променливата c (second_time) вече не съществува, въпреки че променливата b (second_time) съществува като затваряне във вътрешната.

Сега нека да видим какво се случва, когато се изпълнят следните редове от код:

Х(); // X () извика първия път
Х(); // X () се позова втори път
Х(); // X () извика трети път
Y (); // Y () се позова на първия път

Когато X () бъде извикан за първи път,

  1. създава се променлива a и се задава на 20
  2. стойността на a = 20, стойността на b е от стойността на затварянето. b (first_time), така че b = 10
  3. променливите a и b се увеличават с 1
  4. X () завършва изпълнението и всичките му вътрешни променливи - променлива a - престават да съществуват.
    Въпреки това, b (first_time) се запази като затваряне, така че b (first_time) продължава да съществува.

Когато втори път се извика X (),

  1. променлива a се създава отново и се задава на 20
     Всяка предишна стойност на променлива a вече не съществува, тъй като тя престана да съществува, когато X () завърши изпълнението за първи път.
  2. стойността на a = 20
    стойността на b се взема от стойността на затваряне - b (first_time)
    Също така имайте предвид, че бяхме увеличили стойността на b с 1 от предишното изпълнение, така че b = 11
  3. променливите a и b се увеличават отново с 1
  4. X () завършва изпълнението и всичките му вътрешни променливи - променлива a - престават да съществуват
    Въпреки това, b (first_time) се запазва, докато затварянето продължава да съществува.

Когато третия път се извика X (),

  1. променлива a се създава отново и се задава на 20
    Всяка предишна стойност на променлива a вече не съществува, тъй като тя престана да съществува, когато X () завърши изпълнението за първи път.
  2. стойността на a = 20, стойността на b е от стойността на затварянето - b (first_time)
    Също така имайте предвид, че в предишното изпълнение бяхме увеличили стойността на b с 1, така че b = 12
  3. променливите a и b се увеличават отново с 1
  4. X () завършва изпълнението и всичките му вътрешни променливи - променлива a - престават да съществуват
    Въпреки това, b (first_time) се запазва, докато затварянето продължава да съществува

Когато Y () бъде извикан за първи път,

  1. променлива a се създава отново и се задава на 20
  2. стойността на a = 20, стойността на b е от стойността на затварянето - b (второ време), така че b = 10
  3. променливите a и b се увеличават с 1
  4. Y () завършва изпълнението и всичките му вътрешни променливи - променлива a - престават да съществуват
    Въпреки това, b (second_time) се запази като затваряне, така че b (second_time) продължава да съществува.

Заключителни бележки

Затварянията са едно от онези фини понятия в JavaScript, които в началото са трудни за разбиране. Но след като ги разберете, осъзнавате, че нещата не биха могли да бъдат по друг начин.

Надяваме се, че тези стъпка по стъпка обяснения ви помогнаха наистина да разберете концепцията за затваряния в JavaScript!

Други статии:

  • Бързо ръководство за функциите за „самоизвикване“ в Javascript
  • Разбиране на обхвата на функцията спрямо обхвата на блока в Javascript
  • Как да използвате обещания в JavaScript
  • Как да изградите проста Sprite анимация в JavaScript