Co to jest "zamknięcie"?

Zadałem pytanie o Currying i zamknięcia zostały wymienione. Co to jest zamknięcie? Jak to się ma do currying?

Author: Ben, 2008-08-31

23 answers

Zakres zmienny

Kiedy deklarujesz zmienną lokalną, zmienna ta ma zakres. Ogólnie rzecz biorąc, zmienne lokalne istnieją tylko w obrębie bloku lub funkcji, w których je deklarujesz.

function() {
  var a = 1;
  console.log(a); // works
}    
console.log(a); // fails

Jeśli spróbuję uzyskać dostęp do zmiennej lokalnej, większość języków będzie szukać jej w bieżącym zakresie, a następnie w górę Przez zakresy nadrzędne, aż osiągną zakres główny.

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works

Gdy blok lub funkcja jest wykonywana z, jej zmienne lokalne nie są już potrzebne i są zwykle wydmuchiwane z pamięć.

W ten sposób zwykle oczekujemy, że wszystko będzie działać.

Zamknięcie jest stałym zakresem zmiennej lokalnej

Zamknięcie to trwały zakres, który utrzymuje zmienne lokalne nawet po tym, jak wykonanie kodu zostało przeniesione z tego bloku. Języki obsługujące zamknięcie (takie jak JavaScript, Swift i Ruby) pozwolą Ci zachować odniesienie do zakresu (w tym jego zakresów nadrzędnych), nawet po zakończeniu wykonywania bloku, w którym te zmienne zostały zadeklarowane, pod warunkiem, że zachowasz gdzieś odniesienie do tego bloku lub funkcji.

Obiekt scope i wszystkie jego zmienne lokalne są powiązane z funkcją i będą trwać tak długo, jak długo ta funkcja będzie istnieć.

To daje nam możliwość przenoszenia funkcji. Możemy oczekiwać, że wszelkie zmienne, które znajdowały się w zakresie, gdy funkcja została po raz pierwszy zdefiniowana, nadal będą w zakresie, gdy później wywołamy funkcję, nawet jeśli wywołamy funkcję w zupełnie innym kontekście.

Na przykład

Oto bardzo prosty przykład w JavaScript, który ilustruje punkt:

outer = function() {
  var a = 1;
  var inner = function() {
    console.log(a);
  }
  return inner; // this returns a function
}

var fnc = outer(); // execute outer to get inner 
fnc();

Tutaj zdefiniowałem funkcję wewnątrz funkcji. Funkcja wewnętrzna uzyskuje dostęp do wszystkich zmiennych lokalnych funkcji zewnętrznej, w tym a. Zmienna a jest w zakresie funkcji wewnętrznej.

Normalnie, gdy funkcja kończy działanie, wszystkie jej zmienne lokalne są zdmuchiwane. Jeśli jednak zwrócimy wewnętrzną funkcję i przypiszemy ją do zmiennej fnc tak, aby utrzymywała się po zakończeniu outer, wszystkie z zmiennych, które znajdowały się w zasięgu, gdy inner został zdefiniowany, również utrzymują . Zmienna a została zamknięta przez -- znajduje się w zamknięciu.

Zauważ, że zmienna a jest całkowicie prywatna dla fnc. Jest to sposób tworzenia zmiennych prywatnych w funkcyjnym języku programowania, takim jak JavaScript.

Jak można się domyślić, kiedy wywołuję {[11] } wyświetla wartość a, która jest "1".

W języku bez zamknięcia, zmienna a będzie zostały zebrane i wyrzucone po zakończeniu funkcji outer. Wywołanie fnc wywołałoby błąd, ponieważ a już nie istnieje.

W JavaScript zmienna a trwa, ponieważ zakres zmiennej jest tworzony, gdy funkcja jest po raz pierwszy zadeklarowana i trwa tak długo, jak funkcja nadal istnieje.

a należy do zakresu outer. Zakres inner posiada wskaźnik nadrzędny do zakresu outer. {[5] } jest zmienną, która wskazuje na inner. a trwa tak długo, jak fnc trwa. a jest w zamknięciu.

 794
Author: superluminary,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-16 02:02:02

Podam przykład (w JavaScript):

function makeCounter () {
  var count = 0;
  return function () {
    count += 1;
    return count;
  }
}

var x = makeCounter();

x(); returns 1

x(); returns 2

...etc...

Funkcja makeCounter zwraca funkcję, którą nazwaliśmy x, która będzie liczyć się o jeden za każdym razem, gdy zostanie wywołana. Ponieważ nie podajemy żadnych parametrów x musi jakoś zapamiętać liczbę. Wie, gdzie go znaleźć na podstawie tak zwanego zakresu leksykalnego - musi szukać w miejscu, w którym jest zdefiniowany, aby znaleźć wartość. Ta "ukryta" wartość jest tym, co nazywa się zamknięciem.

Oto mój przykład currying znowu:

function add (a) {
  return function (b) {
    return a + b;
  }
}

var add3 = add(3);

add3(4); returns 7

Możesz zobaczyć, że gdy wywołujesz add z parametrem a (który jest 3), ta wartość jest zawarta w zamknięciu zwracanej funkcji, którą definiujemy jako add3. W ten sposób, gdy wywołamy add3 wie, gdzie znaleźć wartość a do wykonania dodawania.

 100
Author: Kyle Cronin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-30 02:20:17

Po pierwsze, wbrew temu, co większość ludzi tutaj mówi, zamknięcie jest , A Nie funkcją ! Więc co to jest ?
Jest to zbiór symboli zdefiniowanych w "otaczającym kontekście" funkcji (znanym jako jej środowisko ), które sprawiają, że jest to wyrażenie zamknięte (to znaczy wyrażenie, w którym każdy symbol jest zdefiniowany i ma wartość, więc można go ocenić).

Na przykład, gdy masz funkcję JavaScript:

function closed(x) {
  return x + 3;
}

Jest to wyrażenie zamknięte ponieważ wszystkie występujące w nim symbole są w nim zdefiniowane( ich znaczenia są jasne), więc można je ocenić. Innymi słowy, jest to samodzielny .

Ale jeśli masz taką funkcję:

function open(x) {
  return x*y + 3;
}

Jest to wyrażenie otwarte, ponieważ istnieją w nim symbole, które nie zostały w nim zdefiniowane. Mianowicie y. Patrząc na tę funkcję, nie możemy powiedzieć, czym jest y i co ona oznacza, nie znamy jej wartości, więc nie możemy Oceń to wyrażenie. Nie możemy wywołać tej funkcji, dopóki nie powiemy, co y ma w niej znaczyć. Ta y nazywa się zmienną wolną.

To y błaga o definicję, ale ta definicja nie jest częścią funkcji – jest zdefiniowana gdzie indziej, w jej "otaczającym kontekście" (znanym również jako środowisko ). Przynajmniej na to liczymy: P

Na przykład, może być zdefiniowana globalnie:

var y = 7;

function open(x) {
  return x*y + 3;
}

Lub może być zdefiniowana w funkcja, która ją otacza:

var global = 2;

function wrapper(y) {
  var w = "unused";

  return function(x) {
    return x*y + 3;
  }
}

Częścią środowiska, która nadaje swobodnym zmiennym w wyrażeniu ich znaczenia, jest zamknięcie. Nazywa się to w ten sposób, ponieważ zamienia otwarte wyrażenie w zamknięte , dostarczając te brakujące definicje dla wszystkich swoich wolnych zmiennych , abyśmy mogli je ocenić.

W powyższym przykładzie funkcja wewnętrzna (której nie podaliśmy nazwy, ponieważ jej nie potrzebowaliśmy) jest wyrażenie otwarte ponieważ zmienna y w niej jest wolna - jej definicja znajduje się poza funkcją, w funkcji, która ją otacza. Środowisko dla tej funkcji anonimowej jest zbiorem zmiennych:

{
  global: 2,
  w: "unused",
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Teraz, zamknięcie jest tą częścią tego środowiska, która zamyka wewnętrzną funkcję przez podanie definicji dla wszystkich jej wolnych zmiennych. W naszym przypadku jedyną zmienną wolną w funkcji wewnętrznej była y, tak więc zamknięciem tej funkcji jest podzbiór jej otoczenia:

{
  y: [whatever has been passed to that wrapper function as its parameter `y`]
}

Pozostałe dwa symbole zdefiniowane w środowisku są Nie częścią zamknięcia tej funkcji, ponieważ nie wymaga ich uruchomienia. Nie są one potrzebne, aby zamknąć to.

Więcej o teorii stojącej za tym tutaj: https://stackoverflow.com/a/36878651/434562

Warto zauważyć, że w powyższym przykładzie funkcja wrapper zwraca swoje wewnętrzne funkcja jako wartość. Moment wywołania tej funkcji może być odległy w czasie od momentu zdefiniowania (lub utworzenia) funkcji. W szczególności, jej funkcja zawijania nie jest już uruchomiona, a jej parametry, które były na stosie wywołań, już nie istnieją :P to sprawia problem, ponieważ wewnętrzna funkcja potrzebuje y, aby być tam, gdy jest wywoływana! Innymi słowy, wymaga od zmiennych zamknięcia, aby w jakiś sposób przetrwały funkcję wrappera i były tam w razie potrzeby. Dlatego wewnętrzna funkcja musi wykonać migawkę tych zmiennych, które zamykają i przechowują je w bezpiecznym miejscu do późniejszego użycia. (Gdzieś poza stosem połączeń.)

I dlatego ludzie często mylą termin zamknięcie jako specjalny typ funkcji, która może wykonywać takie migawki zewnętrznych zmiennych, których używają, lub strukturę danych używaną do przechowywania tych zmiennych na później. Ale mam nadzieję, że teraz rozumiesz, że są Nie closure itself – są to po prostu sposoby zaimplementowania Closure w języku programowania, lub mechanizmy językowe, które pozwalają zmiennym z zamknięcia funkcji być tam, gdy jest to potrzebne. Istnieje wiele błędnych wyobrażeń dotyczących zamknięć, które (niepotrzebnie) sprawiają, że ten temat jest znacznie bardziej zagmatwany i skomplikowany niż w rzeczywistości jest.

 55
Author: SasQ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-20 06:38:12

Zamknięcie jest funkcją, która może odwoływać się do stanu w innej funkcji. Na przykład w Pythonie używa się zamknięcia "inner":

def outer (a):
    b = "variable in outer()"
    def inner (c):
        print a, b, c
    return inner

# Now the return value from outer() can be saved for later
func = outer ("test")
func (1) # prints "test variable in outer() 1
 29
Author: John Millikin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-16 02:08:48

Aby ułatwić zrozumienie zamknięć, przydatne może być zbadanie, w jaki sposób można je zaimplementować w języku proceduralnym. Wyjaśnienie to będzie następowało po uproszczonej implementacji zamknięć w systemie.

Na początek muszę wprowadzić pojęcie przestrzeni nazw. Po wprowadzeniu polecenia do interpretera Scheme, musi on ocenić różne symbole w wyrażeniu i uzyskać ich wartość. Przykład:

(define x 3)

(define y 4)

(+ x y) returns 7

Wyrażenia define przechowują wartość 3 w miejscu dla x i wartość 4 w miejscu dla y. następnie, gdy wywołamy (+ X y), interpreter Wyszukuje wartości w przestrzeni nazw i jest w stanie wykonać operację i zwrócić 7.

Jednak w Scheme są wyrażenia, które pozwalają tymczasowo nadpisać wartość symbolu. Oto przykład:

(define x 3)

(define y 4)

(let ((x 5))
   (+ x y)) returns 9

x returns 3

Słowo kluczowe let wprowadza nową przestrzeń nazw z x jako wartością 5. Zauważysz, że nadal jest w stanie zobaczyć, że y wynosi 4, dzięki czemu suma zwrócona wynosi 9. Możesz Zobacz też, że po zakończeniu wyrażenia x wraca do wartości 3. W tym sensie x został tymczasowo zamaskowany przez wartość lokalną.

Języki proceduralne i obiektowe mają podobne pojęcie. Za każdym razem, gdy zadeklarujesz zmienną w funkcji o tej samej nazwie co zmienna globalna, uzyskasz ten sam efekt.

Jak mielibyśmy to wdrożyć? Prosty sposób jest z połączoną listą - head zawiera nową wartość, a tail zawiera starą przestrzeń nazw. Kiedy trzeba spojrzeć w górę symbol, zaczynasz od głowy i idziesz w dół ogona.

Przejdźmy teraz do implementacji funkcji pierwszej klasy. Mniej więcej, funkcja jest zbiorem instrukcji do wykonania, gdy funkcja jest wywoływana przez wartość zwracaną. Kiedy czytamy w funkcji, możemy przechowywać te instrukcje Za kulisami i uruchamiać je podczas wywoływania funkcji.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns ?

Definiujemy x jako 3, A plus-x jako jego parametr, y, plus wartość x. w końcu wywołanie plus - x w środowisku, w którym x zostało zamaskowane przez nowe x, ten o wartości 5. Jeśli po prostu przechowujemy operację( + X y) dla funkcji plus-x, ponieważ jesteśmy w kontekście X jako 5, zwracany wynik będzie wynosił 9. To się nazywa dynamiczny zasięg.

Jednak Scheme, Common Lisp i wiele innych języków mają tak zwany zakres leksykalny - oprócz przechowywania operacji( + X y) przechowujemy również przestrzeń nazw w tym konkretnym punkcie. W ten sposób, gdy patrzymy w górę wartości możemy zobaczyć, że x, w tym kontekście, jest naprawdę 3. To jest zakończenie.

(define x 3)

(define (plus-x y)
  (+ x y))

(let ((x 5))
  (plus-x 4)) returns 7

Podsumowując, możemy użyć listy połączonej do przechowywania stanu przestrzeni nazw w momencie definiowania funkcji, co pozwala nam na dostęp do zmiennych z zakresów zamykających, a także zapewnia nam możliwość lokalnie zamaskowania zmiennej bez wpływu na resztę programu.

 23
Author: Kyle Cronin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-23 00:21:29

Funkcje nie zawierające wolnych zmiennych nazywa się funkcjami czystymi.

Funkcje zawierające jedną lub więcej zmiennych wolnych nazywa się zamknięciami.

var pure = function pure(x){
  return x 
  // only own environment is used
}

var foo = "bar"

var closure = function closure(){
  return foo 
  // foo is a free variable from the outer environment
}

src: https://leanpub.com/javascriptallongesix/read#leanpub-auto-if-functions-without-free-variables-are-pure-are-closures-impure

 10
Author: soundyogi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Oto przykład z prawdziwego świata, dlaczego zamknięcia dają dupy... To jest prosto z mojego kodu Javascript. Pozwól mi zilustrować.

Function.prototype.delay = function(ms /*[, arg...]*/) {
  var fn = this,
      args = Array.prototype.slice.call(arguments, 1);

  return window.setTimeout(function() {
      return fn.apply(fn, args);
  }, ms);
};

A oto jak byś go użył:

var startPlayback = function(track) {
  Player.play(track);  
};
startPlayback(someTrack);

Teraz wyobraź sobie, że chcesz, aby odtwarzanie rozpoczęło się z opóźnieniem, na przykład 5 sekund później po uruchomieniu tego fragmentu kodu. No to łatwo z delay i to jest zamknięcie:

startPlayback.delay(5000, someTrack);
// Keep going, do other things

Kiedy wywołujesz delay z 5000ms, pierwszy urywek uruchamia się i przechowuje przekazane argumenty w jego zamknięciu. Następnie 5 sekund później, gdy dojdzie do wywołania zwrotnego setTimeout, Zamknięcie nadal zachowuje te zmienne, Więc może wywołać oryginalną funkcję z oryginalnymi parametrami.
Jest to rodzaj currying lub funkcji dekoracji.

Bez zamknięć, musiałbyś jakoś utrzymać stan tych zmiennych poza funkcją, zaśmiecając kod poza funkcją czymś, co logicznie należy do niej. Korzystanie z zamknięć może znacznie poprawić jakość i czytelność kodu.

 9
Author: adamJLev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-16 02:11:33

Tl; dr

Zamknięcie jest funkcją i jej zakresem przypisanym (lub używanym jako) zmiennej. Tak więc nazwa closure: the scope i The function jest zamknięta i używana tak jak każda inna jednostka.

In depth Wikipedia style explanation

Według Wikipedii zamknięcie to:

Techniki implementacji nazw o zasięgu leksykalnym w językach Z FUNKCJAMI pierwszej klasy.

Co to znaczy? Przyjrzyjmy się niektórym definicje.

Wyjaśnię zamknięcia i inne pokrewne definicje, używając tego przykładu:

function startAt(x) {
    return function (y) {
        return x + y;
    }
}

var closure1 = startAt(1);
var closure2 = startAt(5);

console.log(closure1(3)); // 4 (x == 1, y == 3)
console.log(closure2(3)); // 8 (x == 5, y == 3)

Funkcje pierwszej klasy

W zasadzie oznacza to możemy używać funkcji tak jak każdy inny byt . Możemy je modyfikować, przekazywać jako argumenty, zwracać z funkcji lub przypisywać do zmiennych. Technicznie rzecz biorąc, są to obywatele pierwszej klasy , stąd nazwa: funkcje pierwszej klasy.

W powyższym przykładzie, startAt zwraca funkcję ( anonymous ), której funkcje są przypisane do closure1 i closure2. Więc jak widzisz JavaScript traktuje funkcje tak jak wszystkie inne podmioty (obywatele pierwszej klasy).

Name binding

Powiązanie nazwy polega na ustaleniu jakie dane zawiera zmienna (identyfikator) . Zakres jest tutaj naprawdę ważny, ponieważ to jest rzecz, która określi sposób rozwiązania wiązania.

W przykładzie powyżej:

  • w wewnętrznym zakresie funkcji anonimowej, y jest związane z 3.
  • w zakresie startAt, x jest związane z 1 lub 5 (w zależności od zamknięcia).

Wewnątrz zakresu funkcji anonimowej, x nie jest związana z żadną wartością, więc musi być rozwiązana w górnym (startAt) zakresie.

Zakres leksykalny

Jak mówi Wikipedia , zakres:

Jest regionem programu komputerowego, w którym Wiązanie jest ważne: gdzie nazwa może być używana w odniesieniu do podmiotu .

Istnieją dwie techniki:

  • leksykalne (statyczne) zakresy: definicja zmiennej jest rozwiązywana przez przeszukanie jej zawierającego blok lub funkcji, a jeśli to nie powiedzie się, przeszukanie zewnętrznego zawierającego blok itd.
  • dynamiczny zakres: funkcja wywołująca jest wyszukiwana, następnie funkcja, która wywołała tę funkcję wywołującą, i tak dalej, rozwijając stos wywołań.

Więcej Wyjaśnienie, zobacz to pytanie i spójrz na Wikipedię.

W powyższym przykładzie widzimy, że JavaScript ma leksykalny zasięg, ponieważ gdy x jest rozwiązany, powiązanie jest przeszukiwane w górnym (startAt) obszarze, w oparciu o kod źródłowy (anonimowa funkcja, która szuka x jest zdefiniowana wewnątrz startAt), a nie na podstawie stosu wywołań, sposobu (zakresu, w którym) funkcja została wywołana.

Owijanie (zamykanie) w górę

W naszym przykład, gdy wywołamy startAt, zwróci ona (pierwszą klasę) funkcję, która zostanie przypisana do closure1 i closure2 w ten sposób zostanie utworzone zamknięcie, ponieważ przekazane zmienne 1 i 5 zostaną zapisane w zakresie startAt, który zostanie zamknięty ze zwracaną funkcją anonimową. Gdy wywołamy tę funkcję anonimową za pomocą closure1 i closure2 z tym samym argumentem (3), wartość y zostanie znaleziona natychmiast (ponieważ jest to parametr tej funkcji), ale x nie jest związana w zakresie funkcja anonimowa, więc rozdzielczość jest kontynuowana w (leksykalnie) górnym zakresie funkcji (zapisanym w zamknięciu), gdzie x jest związana z 1 lub 5. Teraz wiemy wszystko do podsumowania, więc wynik może zostać zwrócony, a następnie wydrukowany.

Teraz powinieneś zrozumieć zamknięcia i ich zachowanie, co jest podstawową częścią JavaScript.

Currying

A Ty też się nauczyłeś o co chodzi currying: używasz funkcji (closures), aby przekazać każdy argument operacji zamiast używać jednej funkcji z wieloma parametrami.

 7
Author: totymedli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Closure jest funkcją w JavaScript, gdzie funkcja ma dostęp do własnych zmiennych zakresu, dostęp do zewnętrznych zmiennych funkcji i dostęp do zmiennych globalnych.

Closure ma dostęp do zewnętrznego zakresu funkcji nawet po powrocie zewnętrznej funkcji. Oznacza to, że zamknięcie może zapamiętać i uzyskać dostęp do zmiennych i argumentów swojej zewnętrznej funkcji nawet po zakończeniu funkcji.

Wewnętrzna funkcja może uzyskać dostęp do zmiennych zdefiniowanych we własnym zakresie, zakres funkcji zewnętrznej i zakres globalny. A zewnętrzna funkcja może uzyskać dostęp do zmiennej zdefiniowanej we własnym zakresie i globalnym zakresie.

Przykład zamknięcia :

var globalValue = 5;

function functOuter() {
  var outerFunctionValue = 10;

  //Inner function has access to the outer function value
  //and the global variables
  function functInner() {
    var innerFunctionValue = 5;
    alert(globalValue + outerFunctionValue + innerFunctionValue);
  }
  functInner();
}
functOuter();  

Wyjście będzie 20, które suma jego wewnętrznej zmiennej funkcji, zewnętrznej zmiennej funkcji i globalnej wartości zmiennej.

 5
Author: rahul sharma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-16 02:15:00

W normalnej sytuacji zmienne są związane regułą zakresową: zmienne lokalne działają tylko w ramach zdefiniowanej funkcji. Zamknięcie jest sposobem na tymczasowe złamanie tej zasady dla wygody.

def n_times(a_thing)
  return lambda{|n| a_thing * n}
end

W powyższym kodzie {[3] } jest zamknięciem, ponieważ {[4] } jest określany przez lambda (anonimowego twórcę funkcji).

Teraz, jeśli umieścisz wynikową funkcję anonimową w zmiennej funkcyjnej.

foo = n_times(4)

Foo złamie normalną regułę przeszukiwania i zacznie używać 4 wewnętrznie.

foo.call(3)

Zwraca 12.

 4
Author: Eugene Yokota,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-08-31 05:31:57

Krótko mówiąc, wskaźnik funkcji jest tylko wskaźnikiem do lokalizacji w bazie kodu programu (jak licznik programu). Natomiast Zamknięcie = Wskaźnik funkcji + Ramka stosu .

.

 2
Author: RoboAlex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-07 13:50:19

• zamknięcie jest podprogramem i nawiązując do środowiska, w którym było defined

{–1]} - środowisko odniesienia jest potrzebne, jeśli podprogram można wywołać z dowolnego miejsca w Programie {–1]} - Język o statycznym zasięgu, który nie pozwala zagnieżdżać podprogramy nie wymagają zamykania {–1]} - zamknięcia są potrzebne tylko wtedy, gdy podprogram może uzyskać dostęp zmiennych w zakresach zagnieżdżania i może być wywoływana z anywhere

- w celu wsparcia zamknięcia, wdrożenie może potrzeba zapewnić nieograniczony zakres niektórych zmiennych (ponieważ podprogram może uzyskać dostęp do zmiennej nielokalnej, która jest normalnie już nie żyje)

Przykład

function makeAdder(x) {
return function(y) {return x + y;}
}
var add10 = makeAdder(10);
var add5 = makeAdder(5);
document.write(″add 10 to 20: ″ + add10(20) +
″<br />″);
document.write(″add 5 to 20: ″ + add5(20) +
″<br />″);
 1
Author: BoraKurucu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-02 22:10:38

Zamknięcie jest funkcją stanową, która jest zwracana przez inną funkcję. Działa jako kontener do zapamiętywania zmiennych i parametrów z zakresu nadrzędnego, nawet jeśli funkcja nadrzędna zakończyła wykonywanie. Rozważ ten prosty przykład.

  function sayHello() {
  const greeting = "Hello World";

  return function() { // anonymous function/nameless function
    console.log(greeting)
  }
}


const hello = sayHello(); // hello holds the returned function
hello(); // -> Hello World
Spójrz! Mamy funkcję, która zwraca funkcję! Zwracana funkcja zostaje zapisana do zmiennej i wywołana w poniższej linii.
 1
Author: unsuredev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-26 03:59:00

Oto kolejny przykład z życia i używania popularnego w grach języka skryptowego-Lua. Musiałem nieco zmienić sposób działania funkcji bibliotecznych, aby uniknąć problemu z niedostępnością standardowego wejścia.

local old_dofile = dofile

function dofile( filename )
  if filename == nil then
    error( 'Can not use default of stdin.' )
  end

  old_dofile( filename )
end

Wartość old_dofile znika, gdy ten blok kodu kończy swój zakres (ponieważ jest lokalny), jednak wartość została zamknięta w zamknięciu, więc nowa, nowo zdefiniowana funkcja dofile może uzyskać do niego dostęp, a raczej kopię przechowywaną wraz z funkcją jako "upvalue".

 0
Author: Nigel Atkinson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-12 05:57:49

Z Lua.org :

Gdy funkcja jest zapisana zamknięta w innej funkcji, ma ona pełny dostęp do zmiennych lokalnych z funkcji zamykającej; ta funkcja jest nazywana zakresem leksykalnym. Chociaż może to brzmieć oczywiste, nie jest. Zakres leksykalny, plus Funkcje pierwszej klasy, jest potężnym pojęciem w języku programowania, ale niewiele języków obsługuje tę koncepcję.

 0
Author: user5731811,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-30 23:06:50

Jeśli jesteś ze świata Javy, możesz porównać zamknięcie z funkcją klasy. Spójrz na ten przykład

var f=function(){
  var a=7;
  var g=function(){
    return a;
  }
  return g;
}

Funkcja {[1] } jest zamknięciem: g zamyka a w. Tak więc {[1] } może być porównana z funkcją członka, a może być porównana z polem klasy, A Funkcja f z klasą.

 0
Author: ericj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-02 19:35:10

Zamknięcia Gdy mamy funkcję zdefiniowaną wewnątrz innej funkcji, wewnętrzna funkcja ma dostęp do zmiennych zadeklarowanych w funkcji zewnętrznej. Zamknięcia najlepiej wyjaśnić przykładami. W liście 2-18 możesz zobaczyć, że wewnętrzna funkcja ma dostęp do zmiennej (variableInOuterFunction) z zewnętrzny zasięg. Zmienne w funkcji zewnętrznej zostały zamknięte przez (lub związane) funkcję wewnętrzną. Stąd określenie zamknięcie. Koncepcja sama w sobie jest dość prosta i sprawiedliwa intuicyjne.

Listing 2-18:
    function outerFunction(arg) {
     var variableInOuterFunction = arg;

     function bar() {
             console.log(variableInOuterFunction); // Access a variable from the outer scope
     }
     // Call the local function to demonstrate that it has access to arg
     bar(); 
    }
    outerFunction('hello closure!'); // logs hello closure!

Źródło: http://index-of.es/Varios/Basarat%20Ali%20Syed%20(auth.)-Beginning%20Node.js-Apress%20(2014).pdf

 0
Author: shohan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-03 09:15:35

Proszę spojrzeć poniżej kodu, aby zrozumieć zamknięcie bardziej głęboko:

        for(var i=0; i< 5; i++){            
            setTimeout(function(){
                console.log(i);
            }, 1000);                        
        }

Tutaj co będzie wyjście? 0,1,2,3,4 nie będzie to 5,5,5,5,5 z powodu zamknięcia

Więc jak to rozwiąże? Odpowiedź znajduje się poniżej:
       for(var i=0; i< 5; i++){
           (function(j){     //using IIFE           
                setTimeout(function(){
                               console.log(j);
                           },1000);
            })(i);          
        }

Pozwól, że wyjaśnię, Kiedy funkcja utworzona nic się nie dzieje, dopóki nie wywołała tak dla pętli w 1. kodzie wywołanym 5 razy, ale nie wywołanym od razu, więc kiedy wywołała tzn. po 1 sekundzie i również jest to asynchroniczne, więc przed tym dla pętli zakończonej i przechowywać wartość 5 w var i i na koniec wykonaj setTimeout funkcję five time i wydrukuj 5,5,5,5,5

Oto Jak to rozwiązuje używając IIFE czyli natychmiastowego wywołania wyrażenia funkcji

       (function(j){  //i is passed here           
            setTimeout(function(){
                           console.log(j);
                       },1000);
        })(i);  //look here it called immediate that is store i=0 for 1st loop, i=1 for 2nd loop, and so on and print 0,1,2,3,4

Aby dowiedzieć się więcej, proszę zrozumieć kontekst wykonania, aby zrozumieć zamknięcie.

  • Jest jeszcze jedno rozwiązanie, aby rozwiązać to za pomocą let (funkcja ES6), ale pod maską powyżej działa funkcja

     for(let i=0; i< 5; i++){           
         setTimeout(function(){
                        console.log(i);
                    },1000);                        
     }
    
    Output: 0,1,2,3,4
    

= > więcej wyjaśnień:

W pamięci, gdy dla pętli wykonujemy obraz zrób jak poniżej:

Pętla 1)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Pętla 2)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Pętla 3)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Pętla 4)

     setTimeout(function(){
                    console.log(i);
                },1000); 

Pętla 5)

     setTimeout(function(){
                    console.log(i);
                },1000);  

Tutaj i nie jest wykonywane, a następnie po zakończeniu pętli var zapisałem wartość 5 w pamięci, ale jej zakres jest zawsze widoczny w jej funkcji potomnej, więc gdy funkcja wykonuje wewnątrz setTimeout Na Zewnątrz pięć razy wyświetla 5,5,5,5,5

Więc aby rozwiązać to użycie IFE, jak wyjaśniono powyżej.

 0
Author: arun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-15 16:14:22

Currying: pozwala na częściową ocenę funkcji przez podanie tylko podzbioru jej argumentów. Rozważ to:

function multiply (x, y) {
  return x * y;
}

const double = multiply.bind(null, 2);

const eight = double(4);

eight == 8;

Zamknięcie: zamknięcie to nic innego jak dostęp do zmiennej spoza zakresu funkcji. Ważne jest, aby pamiętać, że funkcja wewnątrz funkcji lub funkcja zagnieżdżona nie jest zamknięciem. Zamknięcia są zawsze używane, gdy trzeba uzyskać dostęp do zmiennych poza zakresem funkcji.

function apple(x){
   function google(y,z) {
    console.log(x*y);
   }
   google(7,2);
}

apple(3);

// the answer here will be 21
 0
Author: user11335201,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-04 03:16:42

Zamknięcie jest bardzo łatwe. Możemy to rozważyć w następujący sposób : Closure = function + its lexical environment

Rozważmy następującą funkcję:

function init() {
    var name = “Mozilla”;
}

Jakie będzie zamknięcie w powyższym przypadku ? Funkcja INIT() i zmienne w jej środowisku leksykalnym czyli nazwa. Closure = init () + name

Rozważmy inną funkcję:

function init() {
    var name = “Mozilla”;
    function displayName(){
        alert(name);
}
displayName();
}

Jakie będą zamknięcia tutaj ? Funkcja wewnętrzna może uzyskać dostęp do zmiennych funkcji zewnętrznej. displayName () może uzyskać dostęp do zmiennej nazwa zadeklarowana w funkcji nadrzędnej, init(). Jednak te same zmienne lokalne w displayName () będą używane, jeśli istnieją.

Zamknięcie 1: funkcja init + (zmienna nazwy + funkcja displayName ()) -- > zakres leksykalny

Zamknięcie 2: funkcja displayName + (nazwa zmiennej ) --> zakres leksykalny

 0
Author: Rumel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-29 17:48:59

Zamknięcia dostarczają JavaScript ze stanem.

Stan w programowaniu oznacza po prostu zapamiętywanie rzeczy.

Przykład

var a = 0;

a = a + 1; // => 1
a = a + 1; // => 2
a = a + 1; // => 3

W powyższym przypadku stan jest przechowywany w zmiennej "a". Następnie dodajemy 1 do" a " kilka razy. Możemy to zrobić tylko dlatego, że jesteśmy w stanie "zapamiętać" wartość. Posiadacz państwa, "a", zachowuje tę wartość w pamięci.

Często, w językach programowania, chcesz śledzić rzeczy, zapamiętać informacje i uzyskać do nich dostęp w późniejszym czasie czas.

To, w innych językach , jest powszechnie realizowane poprzez użycie klas. Klasa, podobnie jak zmienne, śledzi swój stan. A instancje tej klasy, z kolei, również mają w sobie stan. Stan oznacza po prostu informacje, które można przechowywać i pobierać później.

Przykład

class Bread {
  constructor (weight) {
    this.weight = weight;
  }

  render () {
    return `My weight is ${this.weight}!`;
  }
}

Jak możemy uzyskać dostęp do" weight "z poziomu metody" render"? Dzięki stanowi. Każda instancja klasy Bread może oddawać swoją wagę, czytając ją z "Państwa", miejsca w pamięci, gdzie moglibyśmy przechowywać te informacje.

Teraz, JavaScript jest bardzo unikalnym językiem , który historycznie nie ma klas (teraz ma, ale pod maską są tylko funkcje i zmienne), Więc zamknięcia zapewniają JavaScript sposób na zapamiętanie rzeczy i dostęp do nich później.

Przykład

var n = 0;
var count = function () {
  n = n + 1;
  return n;
};

count(); // # 1
count(); // # 2
count(); // # 3

Powyższy przykład osiągnął cel "utrzymania stanu" ze zmienną. To jest świetne! Ma to jednak tę wadę, że zmienna (posiadacz "stanu") jest teraz odsłonięta. Stać nas na więcej. Możemy użyć zamknięć.

Przykład

var countGenerator = function () {
  var n = 0;
  var count = function () {
    n = n + 1;
    return n;
  };

  return count;
};

var count = countGenerator();
count(); // # 1
count(); // # 2
count(); // # 3
To jest fantastyczne.

Teraz nasza funkcja "count" może liczyć. Jest w stanie to zrobić tylko dlatego, że może "utrzymać" stan. Stanem w tym przypadku jest zmienna "n". Ta zmienna jest teraz zamknięta. Zamknięty w czasie i przestrzeni. W czasie, ponieważ nigdy nie będziesz w stanie go odzyskać, zmienić, przypisać wartości lub bezpośrednio z nim współpracować. W przestrzeni, bo geograficznie zagnieżdżone w funkcji "countGenerator".

Dlaczego to jest fantastyczne? Ponieważ bez angażowania innych zaawansowanych i skomplikowanych narzędzi (np. klas, metod, instancji itp.) jesteśmy w stanie 1. Ukryj 2. sterowanie z odległości

Ukrywamy stan, zmienną "n", co czyni ją zmienną prywatną! Stworzyliśmy również API, które może kontrolować tę zmienną w predefiniowany sposób. W szczególności możemy wywołać API w ten sposób "count ()" i to dodaje 1 do " n " Z a "odległość". W żaden sposób, shape ani form nikt nie będzie mógł uzyskać dostępu do "n" z wyjątkiem interfejsu API.

JavaScript jest naprawdę niesamowity w swojej prostocie.

[[6]}zamknięcia są dużą częścią tego, dlaczego tak jest.
 0
Author: Claudio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-19 14:44:41

Prosty przykład w Groovy w celach informacyjnych:

def outer() {
    def x = 1
    return { -> println(x)} // inner
}
def innerObj = outer()
innerObj() // prints 1
 0
Author: GraceMeng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-09-26 22:12:51