JavaScript Closures пример и обяснение

Намерих този пример за затваряне на codeproject, но не обяснява как работи.

    function getMultiplier(multiplyBy){
       function multiply(num){
          return multiplyBy * num;
       }
 
       return multiply;
    }
 
    var multiplyByTwo = getMultiplier(2);
    var multiplyByTen = getMultiplier(10);
    var twoIntoFive = multiplyByTwo(5); 
    var tenIntoSix = multiplyByTen(6);  

    console.log(twoIntoFive); // 10
    console.log(tenIntoSix); // 60

Сега ще предположа с моя C мозък какво се случва. Моля, поправете ме или дайте вашето обяснение.

  1. функциите в javascript също могат да бъдат обекти, така че вътрешната функция multiply(num) е метод на getMultiplier, гледан отвън.
  2. var multiplyByTwo се присвоява върнатата стойност на функцията getMultiplier, когато се извиква с аргумент 2.
  3. когато извикате getMultiplier(2), javascript интерпретаторът създава обект в паметта въз основа на дефиницията на getMultiplier().
  4. този обект има метод multiply и неговият адрес е присвоен на променлива multiplyByTwo.
  5. var twoIntoFive = multiplyByTwo(5); извиква метода на обекта getMultiplier(2) multiply(num) с аргумент 5.
  6. който връща просто число 10 в променлива twoIntoFive
  7. multiplyByTwo = 0; ще накара събирача на отпадъци на javascript да изтрие обект getMultiplier(2) от паметта.

person Community    schedule 01.08.2017    source източник
comment
Разгледайте тук Как работят затварянията на JavaScript?   -  person Yordan Nikolov    schedule 01.08.2017
comment
Можете да се обърнете към тази връзка, за да научите повече за затварянията. developer.mozilla.org/en/docs/Web/JavaScript/Closures   -  person Rajesh P    schedule 01.08.2017
comment
да Просто method е погрешно използван тук (и обект, ако е в смисъла на js обект) @downvoter сериозно ли??   -  person Jonas Wilms    schedule 01.08.2017
comment
@georg ненужно затваряне. Иска се повторно отваряне. Свързаният въпрос обяснява как работят затварянията, но не отговаря на конкретни въпроси на op.   -  person Tom M    schedule 01.08.2017
comment
@TomM: дупката изглежда изчерпателна и се съмнявам, че има какво ново да се каже, но нека опитаме!   -  person georg    schedule 01.08.2017
comment
@georg благодаря, разбирам мнението ви и съм съгласен, че е доста изчерпателно по отношение на затварянията. Но както казах, OP имаше конкретни въпроси относно неговата конкретна част от кода и се съмнявам, че измамникът отговаря на всички тях. Не исках да изглеждам груб, но според мен много въпроси се затварят твърде бързо, което прави stackoverflow доста токсичен за новите потребители   -  person Tom M    schedule 01.08.2017


Отговори (3)


Интересни въпроси.

  1. Почти правилно, освен че на основно ниво, всъщност в javascript всички функции са обекти. Освен това, разглеждайки последните две променливи, twoIntoFive и tenIntoSix, multiply() е самостоятелна функция в обхвата на getMultiplier() (което означава, че съдържа и препратка към променливи вътре в тази функция).

  2. Точно

  3. Правилно, но имайте предвид, че все още се различава от getMultiplier.

  4. Както е обяснено в тази статия, а не обект, javascript създава интерфейс с препратка към вече декларираните променливи във функцията.

  5. Ето как изглежда работи. Но twoIntoFive всъщност извиква върнатия интерфейс, който препраща към параметъра на функцията getMultiplier()

  6. Правилно.

  7. Това е вярно. Тъй като сте премахнали препратката към интерфейса, тя ще бъде изтрита.

Допълнителна литература

Като цяло е добра идея да се обърнете към MDN, когато имате въпроси относно javascript. Това е най-пълната документация, която съм чел досега.

Чувствайте се свободни да ме коригирате или да дадете обратна връзка, ако намерите грешки в отговора ми или имате допълнителни въпроси

person Tom M    schedule 01.08.2017
comment
Сгреших за 3, 4 и 5. тъй като C няма вложени функции, предположих, че обект на getMultiplier е създаден в купчината с неговите членски променливи, но това не ви дава достъп до аргумента. Извикването на Pascal към вложена функция има една допълнителна информация в рамката на стека на вложената функция, която е указателят към рамката на стека на външната функция. имате нужда от два указателя, за да извикате вложена функция, те се предават на multiplyByTwo като върната стойност от getMultiplier(multiplyBy). един за рамката на стека на getMultiplier(2), а другият за multiply(num). - person ; 02.08.2017
comment
събирачът на отпадъци маркира, че multiplyByTwo има препратка към този кадър на стека и не го освобождава автоматично при връщане от getMultiplier(2). тогава извикването на multiplyByTwo(5) се равнява на multiply(5), предавайки указателя към рамката на стека на getMultiplier(2). това ли може да се каже, че се случва тук? - person ; 02.08.2017
comment
При мен линка не работи. Да, възможността за препращане и изпълнение на функция, докато декларирате променлива, е някак объркваща, - person Tom M; 03.08.2017
comment
(...) тогава извикване на multiplyByTwo(5) се равнява на multiply(5) предаване на указателя към рамката на стека на getMultiplier(2). това ли може да се каже, че се случва тук? Не съм сигурен за това, но изглежда така, когато стартирате скрипта в програмата за отстраняване на грешки. - person Tom M; 03.08.2017
comment
това обяснение на вложените функции е най-доброто за разбиране на затварянията drdobbs.com/architecture-and-design/ - person ; 04.08.2017

Всъщност multiply е просто функция, а не метод от гледна точка на ООП.

По принцип функциите са обекти, но извикваеми. В този случай не става въпрос дали функцията е обект, а повече дали можете да предадете препратка към функция. Това е възможно и се прави с return multiply.

След извикване на getMultiplier със стойност за параметъра, стойността се съхранява и се връща функция (и се присвоява).

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

Локалната променлива multiplyBy се използва за връщане на резултат. Не е създаден обект.

person Nina Scholz    schedule 01.08.2017
comment
Но multiplyByTwo и multiplyByTen препращат към една и съща функция multiply(num), ако в паметта не са създадени обекти, къде multiply(num) получава съответната стойност на multiplyBy? Просто извикването на референция на multiply(num) ще има multipleBy недефинирано. - person ; 01.08.2017
comment
той генерира нов локален обхват за променливите вътре. с връщането на функцията, обхватът се запазва за нея. - person Nina Scholz; 01.08.2017
comment
@b'stard - не, те препращат към различни функции. Функцията (или обектите, ако искате да ги наречете така, каквито са) се създават и връщат всеки път, когато извикате getMultiplier. Направете това getMultiplier(2) == getMultiplier(10) - невярно е - те са различни обекти - person Adam; 01.08.2017
comment
Нов локален обхват за променливите вътре въз основа на дефиницията на getMultiplier(multiplyBy) е някакъв вид обект в паметта. Но къде? И какъв символ използва, за да посочи това място в паметта? Освен това създава две от тези в паметта. Един за multiplyByTwo и един за multiplyByTen. Те трябва да са символите за новите обекти, до които multiply(num) има достъп. Вътрешното представяне на вътрешната функция multiply(num) трябва да има някакъв скрит указател към getMultiplier обекти, точно както C++ функциите имат скрития this указател на обекта. - person ; 01.08.2017
comment
Това може да е семантика, но multiply не трябва да знае за getMultiplier сам по себе си, той просто трябва да знае какво е налично в неговия локален обхват (който случайно е същият като обхвата, създаден, когато getMultiplier бъде извикан). Времето за изпълнение на JS очевидно следи този обхват, но не съм срещал двигател, който да предоставя механизъм за достъп до обхвата. - person Adam; 01.08.2017
comment
@Adam - нещо такова. Използвам термина C за обект - тип данни, който заема реално място в паметта. - person ; 01.08.2017

По принцип функцията си е функция. Не бих казал, че multiply(num) е метод на getMultiplier, той просто е деклариран в тялото си.

Може би по този начин ще бъде по-лесно за разбиране?

var x = function getMultiplier(multiplyBy){
   var y = function (num){
      return multiplyBy * num;
   }

   return y;
}

Както и да е. Начинът, по който работи е: извикването на getMultiplier връща функция "multiply". Функцията за умножение връща multiplyBy * num, но getMultiplier връща нещо като предварително направена функция за умножение, която multiplyBy вече е заместена от параметъра на getMultiplier.

Така, когато извикате getMultiplier(2), това, което получавате под променливата multiplyByTwo е:

function multiply(num){
   return 2 * num;
}

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

person Scharnvirk    schedule 01.08.2017