it-swarm-eu.dev

Jak funguje uzavření JavaScriptu?

Jak byste vysvětlili uzavření JavaScriptu někomu, kdo má znalosti o pojmech, z nichž se skládají (například funkce, proměnné a podobně), ale nerozumí samotným uzávěrkám?

Viděl jsem příklad Scheme uvedený na Wikipedii, ale bohužel to nepomohlo.

7650
e-satis

Uzávěry JavaScriptu pro začátečníky

Vložil Morris dne Út, 2006-02-21 10:19. Od té doby editováno ve Společenství.

Uzávěry nejsou magické

Na této stránce jsou vysvětleny uzávěry, aby jim programátor mohl porozumět - pomocí funkčního kódu JavaScript. Není to pro guru nebo funkční programátory.

Uzávěry jsou není těžké pochopit, jakmile je základní koncept grokkován. Nelze je však pochopit čtením teoretických nebo akademicky orientovaných vysvětlení!

Tento článek je určen pro programátory s některými zkušenostmi s programováním v běžném jazyce a kteří si mohou přečíst následující funkce JavaScriptu:

function sayHello(name) {
  var text = 'Hello ' + name;
  var say = function() { console.log(text); }
  say();
}
sayHello('Joe');

Dvě stručná shrnutí

  • Když funkce (foo) deklaruje jiné funkce (bar a baz), rodina lokálních proměnných vytvořená v foo je nezničena když funkce skončí. Proměnné jsou pro vnější svět neviditelné. foo může proto chytře vrátit funkce bar a baz, a mohou pokračovat ve čtení, psaní a komunikaci mezi sebou prostřednictvím této uzavřené rodiny proměnných ("uzavření"), se kterou se nikdo jiný nemůže zabývat, ani někdo, kdo volá foo opět v budoucnu.

  • Uzavření je jedním ze způsobů podpory funkce první třídy ; je to výraz, který může odkazovat na proměnné v rámci jeho rozsahu (když byl poprvé deklarován), přiřazen k proměnné, předán jako argument funkci nebo být vrácen jako výsledek funkce.

Příklad uzavření

Následující kód vrátí odkaz na funkci:

function sayHello2(name) {
  var text = 'Hello ' + name; // Local variable
  var say = function() { console.log(text); }
  return say;
}
var say2 = sayHello2('Bob');
say2(); // logs "Hello Bob"

Většina programátorů JavaScriptu pochopí, jak je odkaz na funkci vrácen proměnné (say2) ve výše uvedeném kódu. Pokud to neuděláte, musíte se na to podívat dříve, než se můžete dozvědět uzávěry. Programátor používající C by myslel na funkci jako vracení ukazatele na funkci a proměnné say a say2 byly vždy ukazatelem na funkci.

Existuje kritický rozdíl mezi ukazatelem C a funkcí jazyka JavaScript. V JavaScriptu si můžete představit proměnnou funkce, která má jak ukazatel na funkci tak i jako skrytý ukazatel na uzavření.

Výše uvedený kód má uzavření, protože anonymní funkce function() { console.log(text); } je v tomto příkladu deklarována uvnitř další funkce sayHello2(). Pokud v jazyce JavaScript používáte klíčové slovo function uvnitř jiné funkce, vytváříte uzávěr.

V C a ve většině ostatních běžných jazyků, po a funkce se vrátí, všechny lokální proměnné již nejsou přístupné, protože je stack-frame zničen.

Pokud v JavaScriptu deklarujete funkci v rámci jiné funkce, pak lokální proměnné vnější funkce mohou zůstat přístupné i po návratu z ní. To je demonstrováno výše, protože funkci say2() nazýváme po návratu z sayHello2(). Všimněte si, že kód, který voláme, odkazuje na proměnnou text, která byla lokální proměnná funkce sayHello2().

function() { console.log(text); } // Output of say2.toString();

Při pohledu na výstup say2.toString() můžeme vidět, že kód odkazuje na proměnnou text. Anonymní funkce může odkazovat text který drží hodnotu 'Hello Bob' protože místní proměnné sayHello2() byly tajně udržovány živý v uzavření.

Genius je, že v JavaScriptu má odkaz na funkci také tajný odkaz na uzávěrku, ve které byl vytvořen - podobně jako delegáti jsou ukazatelem metody plus tajným odkazem na objekt.

Další příklady

Z nějakého důvodu se uzávěry zdají být těžké pochopit, když čtete o nich, ale když vidíte nějaké příklady, je jasné, jak fungují (trvalo mi to chvíli). Doporučuji pracovat s příklady pozorně, dokud neporozumíte, jak fungují. Pokud začnete používat uzávěry, aniž byste plně pochopili, jak fungují, brzy byste vytvořili velmi podivné chyby!

Příklad 3

Tento příklad ukazuje, že lokální proměnné nejsou kopírovány - jsou uchovávány odkazem. Je to, jako by stoh rám zůstal naživu v paměti i po ukončení vnější funkce!

function say667() {
  // Local variable that ends up within closure
  var num = 42;
  var say = function() { console.log(num); }
  num++;
  return say;
}
var sayNumber = say667();
sayNumber(); // logs 43

Příklad 4

Všechny tři globální funkce mají společný odkaz na stejný uzavření, protože všechny jsou deklarovány v rámci jednoho volání setupSomeGlobals().

var gLogNumber, gIncreaseNumber, gSetNumber;
function setupSomeGlobals() {
  // Local variable that ends up within closure
  var num = 42;
  // Store some references to functions as global variables
  gLogNumber = function() { console.log(num); }
  gIncreaseNumber = function() { num++; }
  gSetNumber = function(x) { num = x; }
}

setupSomeGlobals();
gIncreaseNumber();
gLogNumber(); // 43
gSetNumber(5);
gLogNumber(); // 5

var oldLog = gLogNumber;

setupSomeGlobals();
gLogNumber(); // 42

oldLog() // 5

Tyto tři funkce mají společný přístup ke stejnému uzavření - lokální proměnné setupSomeGlobals() když byly definovány tři funkce.

Všimněte si, že ve výše uvedeném příkladu, pokud zavoláte setupSomeGlobals() znovu, pak je vytvořen nový uzávěr (stack-frame!). Staré proměnné gLogNumber, gIncreaseNumber, gSetNumber jsou přepsány new funkcí, které mají nový uzávěr. (V JavaScriptu, kdykoliv deklarujete funkci uvnitř jiné funkce, je vnitřní funkce znovu vytvořena každá čas, kdy je volána vnější funkce.)

Příklad 5

Tento příklad ukazuje, že uzávěr obsahuje všechny lokální proměnné, které byly deklarovány uvnitř vnější funkce před jejím ukončením. Všimněte si, že proměnná alice je vlastně deklarována po anonymní funkci. Anonymní funkce je deklarována jako první a když je tato funkce volána, může přistupovat k proměnné alice, protože alice je ve stejném oboru (JavaScript dělá variabilní zvedání ). Také sayAlice()() právě přímo volá odkaz funkce vrácený z sayAlice() - je to přesně to, co bylo provedeno dříve, ale bez dočasné proměnné.

function sayAlice() {
    var say = function() { console.log(alice); }
    // Local variable that ends up within closure
    var alice = 'Hello Alice';
    return say;
}
sayAlice()();// logs "Hello Alice"

Tricky: Všimněte si, že proměnná say je také uvnitř uzávěru a může být zpřístupněna jakoukoliv jinou funkcí, která může být deklarována v rámci sayAlice(), nebo může být přístupná rekurzivně uvnitř vnitřní funkce.

Příklad 6

Tohle je pro mnoho lidí opravdová věc, takže ji musíte pochopit. Buďte velmi opatrní, pokud definujete funkci v rámci smyčky: místní proměnné z uzávěru nemusí fungovat tak, jak byste si mohli myslet.

Abyste pochopili tento příklad, musíte pochopit funkci "variabilní zvedání" v jazyce Javascript.

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        var item = 'item' + i;
        result.Push( function() {console.log(item + ' ' + list[i])} );
    }
    return result;
}

function testList() {
    var fnlist = buildList([1,2,3]);
    // Using j only to help prevent confusion -- could use i.
    for (var j = 0; j < fnlist.length; j++) {
        fnlist[j]();
    }
}

 testList() //logs "item2 undefined" 3 times

Řádek result.Push( function() {console.log(item + ' ' + list[i])} přidá třikrát odkaz na anonymní funkci do pole výsledků. Pokud nejste obeznámeni s anonymními funkcemi, považujte to za:

pointer = function() {console.log(item + ' ' + list[i])};
result.Push(pointer);

Všimněte si, že při spuštění příkladu je "item2 undefined" zaznamenáno třikrát! Je to proto, že stejně jako v předchozích příkladech existuje pouze jedno uzavření lokálních proměnných pro buildList (což jsou result, i a item). Při volání anonymních funkcí na řádku fnlist[j](); všechny používají stejný jediný uzávěr a používají v rámci jednoho uzavření aktuální hodnotu i a item (kde i má hodnotu 3, protože smyčka byla dokončena a item má hodnotu 'item2'). Poznámka: indexujeme od 0, proto má item hodnotu item2. A i ++ zvýší i na hodnotu 3.

Může být užitečné zjistit, co se stane, když se použije deklarace proměnné item na úrovni bloku (prostřednictvím klíčového slova let) namísto deklarace proměnné funkce s rozlišením prostřednictvím klíčového slova var. Pokud je tato změna provedena, pak každá anonymní funkce v poli result má své vlastní uzavření; když je příklad spuštěn, výstup je následující:

item0 undefined
item1 undefined
item2 undefined

Pokud je proměnná i také definována pomocí let namísto var, pak je výstup:

item0 1
item1 2
item2 3

Příklad 7

V tomto posledním příkladu každé volání hlavní funkce vytvoří samostatné uzavření.

function newClosure(someNum, someRef) {
    // Local variables that end up within closure
    var num = someNum;
    var anArray = [1,2,3];
    var ref = someRef;
    return function(x) {
        num += x;
        anArray.Push(num);
        console.log('num: ' + num +
            '; anArray: ' + anArray.toString() +
            '; ref.someVar: ' + ref.someVar + ';');
      }
}
obj = {someVar: 4};
fn1 = newClosure(4, obj);
fn2 = newClosure(5, obj);
fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4;
fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4;
obj.someVar++;
fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5;
fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Souhrn

Pokud se vše zdá být naprosto nejasné, pak je nejlepší hrát si s příklady. Čtení vysvětlení je mnohem těžší než příklady porozumění. Moje vysvětlení uzávěrů a skládacích rámů atd. Nejsou technicky správná - jsou to hrubá zjednodušení, která mají pomoci pochopit. Jakmile je základní myšlenka grokkována, můžete si detaily vyzvednout později.

Konečné body:

  • Kdykoliv použijete function uvnitř jiné funkce, použije se uzávěr.
  • Kdykoliv použijete funkci eval() uvnitř funkce, použije se uzávěr. Text, který eval můžete odkazovat na lokální proměnné funkce, a v rámci eval můžete dokonce vytvořit nové lokální proměnné pomocí eval('var foo = …')
  • Pokud používáte new Function(…) ( Function konstruktor ) uvnitř funkce, nevytváří uzávěr. (Nová funkce nemůže odkazovat na lokální proměnné vnější funkce.)
  • Uzavření v JavaScriptu je jako ponechání kopie všech lokálních proměnných, stejně jako při ukončení funkce.
  • Je pravděpodobně nejlepší si myslet, že uzavření je vždy vytvořeno pouze vstupem do funkce a místní proměnné jsou přidány k tomuto uzavření.
  • Nová sada lokálních proměnných se udržuje pokaždé, když je volána funkce s uzavřením (vzhledem k tomu, že funkce obsahuje deklaraci funkce uvnitř ní a odkaz na vnitřní funkci je buď vrácen, nebo je pro ni nějakým způsobem uchováván externí odkaz) ).
  • Dvě funkce mohou vypadat, že mají stejný zdrojový text, ale mají zcela odlišné chování, protože mají „skryté“ uzavření. Nemyslím si, že JavaScript kód může skutečně zjistit, zda funkce odkaz má uzavření nebo ne.
  • Pokud se pokoušíte provést jakékoli úpravy dynamického zdrojového kódu (například: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), nebude fungovat, pokud je myFunction uzavřením (samozřejmě byste nikdy nenapadlo, že byste provedli substituci zdrojového kódu za běhu, ale ...) ).
  • Funkční deklarace je možné získat v rámci funkčních deklarací v rámci funkcí… a můžete uzavřít více než jednu úroveň.
  • Myslím, že obvykle je uzávěr termín pro funkci spolu s proměnnými, které jsou zachyceny. Všimněte si, že tuto definici v tomto článku nepoužívám!
  • Mám podezření, že uzávěry v JavaScriptu se liší od těch, které se běžně nacházejí ve funkčních jazycích.

Odkazy

Dík

Máte-li just naučené uzávěry (zde nebo jinde!), Pak mám zájem o jakoukoli zpětnou vazbu od vás ohledně jakýchkoli změn, které by mohly být pro tento článek jasnější. Pošlete e-mail na adresu morrisjohns.com (morris_closure @). Vezměte prosím na vědomí, že nejsem guru na JavaScriptu - ani na uzávěry.


Původní příspěvek Morris lze nalézt v Internet Archive .

6788
Joel Anair

Kdykoliv vidíte klíčové slovo funkce v rámci jiné funkce, má vnitřní funkce přístup k proměnným ve vnější funkci.

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

To bude vždy log 16, protože bar může přistupovat k x, který byl definován jako argument foo, a může také přistupovat k tmp z foo.

Toisa uzavření. Funkce nemusí mít návrat, aby mohla být nazývána uzavřením.Jednoduchý přístup k proměnným mimo váš bezprostřední lexikální rozsah vytvoří uzavření.

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

Výše uvedená funkce bude také log 16, protože bar může stále odkazovat na x a tmp, i když již není přímo v oboru.

Jelikož však tmp je stále zavěšeno uvnitř bar 's uzavřením, je také zvýšeno. Při každém volání bar se bude zvyšovat.

Nejjednodušším příkladem uzavření je:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

Když je vyvolána funkce JavaScript, vytvoří se nový kontext provádění. Spolu s argumenty funkce a nadřazeným objektem tento kontext provádění také přijímá všechny proměnné deklarované mimo něj (ve výše uvedeném příkladu jsou to 'a' i 'b').

Je možné vytvořit více než jednu uzavírací funkci, a to buď vrácením seznamu nebo nastavením globálních proměnných. To vše bude odkazovat na stejné x a stejné tmp, které nevytvářejí vlastní kopie.

Zde je číslo x doslovné číslo. Stejně jako u jiných literálů v JavaScriptu, když je voláno foo, je číslo x zkopírováno do foo jako jeho argument x.

Na druhou stranu JavaScript při práci s objekty vždy používá odkazy. Pokud řeknete, že jste nazvali foo s objektem, uzávěrka vrátí bude reference ten původní objekt!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Jak bylo očekáváno, každé volání bar(10) zvýší x.memb. Neočekává se, že x jednoduše odkazuje na stejný objekt jako proměnná age! Po několika voláních do bar, age.memb bude 2! Toto odkazování je základem pro nevracení paměti s objekty HTML.

3896
Ali

PŘEDMLUVA: tato odpověď byla napsána při otázce:

Stejně jako starý Albert říkal: "Pokud to nedokážete vysvětlit šestiletému, tak tomu opravdu nerozumíte." No, snažil jsem se vysvětlit JS uzávěry 27letému kamarádovi a zcela selhal.

Může se někdo domnívat, že jsem 6 a podivný zájem o toto téma?

Jsem si docela jistý, že jsem byl jedním z jediných lidí, kteří se pokoušeli vzít první otázku doslova. Od té doby se otázka několikrát zmutovala, takže moje odpověď se teď může zdát neuvěřitelně hloupá a nemístná. Doufejme, že obecná představa příběhu je pro některé zábavná.


Jsem velkým fanouškem analogie a metafory, když vysvětluji obtížné pojmy, takže mi dovolte, abych si zkusila ruku s příběhem.

Bylo nebylo:

Byla tam princezna ...

function princess() {

Žila v nádherném světě plném dobrodružství. Setkala se se svým princem Okouzlujícím, jel kolem jejího světa na jednorožec, bojoval s draky, setkal se s mluvícími zvířaty a spoustou dalších fantastických věcí.

    var adventures = [];

    function princeCharming() { /* ... */ }

    var Unicorn = { /* ... */ },
        dragons = [ /* ... */ ],
        squirrel = "Hello!";

    /* ... */

Vždy by se však musela vrátit zpět do svého tupého světa práce a dospělých.

    return {

A často jim vypráví o svém posledním úžasném dobrodružství jako princezna.

        story: function() {
            return adventures[adventures.length - 1];
        }
    };
}

Ale všechno, co uvidí, je malá holčička ...

var littleGirl = princess();

... vyprávění příběhů o magii a fantazii.

littleGirl.story();

A i když dospělí věděli o skutečných princeznách, nikdy nevěřili v jednorožce nebo draky, protože je nikdy neviděli. Dospělí říkali, že existují jen uvnitř představivosti malé holčičky.

Ale známe pravdu; že ta holčička s princeznou uvnitř ...

... je opravdu princezna s malou holčičkou uvnitř.

2334
Jacob Swartwood

Když vezmeme tuto otázku vážně, měli bychom zjistit, co je typický šestiletý člověk schopný kognitivně, i když je pravda, že ten, kdo má zájem o JavaScript, není tak typický.

On Vývoj v dětství: 5 až 7 let říká:

Vaše dítě bude moci postupovat podle dvou kroků. Pokud například řeknete svému dítěti: "Jděte do kuchyně a vezměte mi pytel na odpadky", budou si moci tento směr pamatovat.

Tento příklad můžeme použít k vysvětlení uzávěrů takto:

Kuchyně je uzávěr, který má lokální proměnnou, nazvanou trashBags. V kuchyni je funkce getTrashBag, která dostane jeden odpadkový pytel a vrátí jej.

Tento kód můžeme v kódu JavaScript kódovat takto:

function makeKitchen() {
  var trashBags = ['A', 'B', 'C']; // only 3 at first

  return {
    getTrashBag: function() {
      return trashBags.pop();
    }
  };
}

var kitchen = makeKitchen();

console.log(kitchen.getTrashBag()); // returns trash bag C
console.log(kitchen.getTrashBag()); // returns trash bag B
console.log(kitchen.getTrashBag()); // returns trash bag A

Další body, které vysvětlují, proč jsou uzávěry zajímavé:

  • Pokaždé, když se nazývá makeKitchen(), vytvoří se nový uzávěr s vlastním samostatným trashBags.
  • Proměnná trashBags je lokální uvnitř každé kuchyně a není přístupná venku, ale vnitřní funkce vlastnosti getTrashBag má k ní přístup.
  • Každé volání funkce vytváří uzávěr, ale nebylo by nutné udržovat uzávěr kolem, pokud vnitřní funkce, která má přístup do vnitřku uzávěru, nemůže být vyvolána zvenku uzávěru. Vrácení objektu s funkcí getTrashBag to provede zde.
719
dlaliberte

Muž slámy

Potřebuji vědět, kolikrát bylo tlačítko kliknuto a udělat něco pro každé třetí kliknutí ...

Docela zjevné řešení

// Declare counter outside event handler's scope
var counter = 0;
var element = document.getElementById('button');

element.addEventListener("click", function() {
  // Increment outside counter
  counter++;

  if (counter === 3) {
    // Do something every third time
    console.log("Third time's the charm!");

    // Reset counter
    counter = 0;
  }
});
<button id="button">Click Me!</button>

Teď to bude fungovat, ale zasahuje do vnějšího rozsahu přidáním proměnné, jejíž jediným účelem je sledovat počet. V některých situacích by to bylo vhodnější, protože vaše vnější aplikace mohou potřebovat přístup k těmto informacím. Ale v tomto případě se mění pouze chování každého třetího kliknutí, takže je vhodnější uzavřít tuto funkci uvnitř obslužného programu události .

Zvažte tuto možnost

var element = document.getElementById('button');

element.addEventListener("click", (function() {
  // init the count to 0
  var count = 0;

  return function(e) { // <- This function becomes the click handler
    count++; //    and will retain access to the above `count`

    if (count === 3) {
      // Do something every third time
      console.log("Third time's the charm!");

      //Reset counter
      count = 0;
    }
  };
})());
<button id="button">Click Me!</button>

Všimněte si pár věcí.

Ve výše uvedeném příkladu používám uzavírání JavaScriptu. Toto chování umožňuje libovolné funkci mít přístup k rozsahu, ve kterém byl vytvořen, na neurčito. Chcete-li to prakticky aplikovat, okamžitě zavolám funkci, která vrací jinou funkci, a protože funkce, kterou vracím, má přístup k interní proměnné počtu (kvůli výše popsanému chování při uzavírání), výsledkem je soukromý rozsah použití. výslednou funkcí ... Není to tak jednoduché? Pojďme to zředit ...

Jednoduché uzavření jednoho řádku

//          _______________________Immediately invoked______________________
//         |                                                                |
//         |        Scope retained for use      ___Returned as the____      |
//         |       only by returned function   |    value of func     |     |
//         |             |            |        |                      |     |
//         v             v            v        v                      v     v
var func = (function() { var a = 'val'; return function() { alert(a); }; })();

Všechny proměnné mimo vrácené funkce jsou k dispozici vrácené funkci, ale nejsou přímo k dispozici objekt vrácené funkce ...

func();  // Alerts "val"
func.a;  // Undefined

Pochopit to? Takže v našem primárním příkladu je proměnná count obsažena v uzávěru a vždy k dispozici obslužnému programu události, takže si zachovává svůj stav od kliknutí po kliknutí.

Tento stav soukromých proměnných je také plně přístupný, pro obě čtení a přiřazení jeho soukromých proměnných.

Tady máš; toto chování nyní plně zapouzdřujete.

Úplný příspěvek do blogu(včetně úvah o jQuery)

556
jondavidjohn

Zatížení je těžké vysvětlit, protože jsou používány k tomu, aby fungovaly nějaké práce, které všichni stejně intuitivně očekávají. Najdu nejlepší způsob, jak je vysvětlit (a způsob, jakýmInaučili se, co dělají) je představit si situaci bez nich:

    var bind = function(x) {
        return function(y) { return x + y; };
    }
    
    var plus5 = bind(5);
    console.log(plus5(3));

Co by se stalo, kdyby JavaScript ne vědět uzavření? Stačí nahradit volání v posledním řádku jeho metodou tělo (což je v podstatě to, co funkce volání) a dostanete:

console.log(x + 3);

Kde je definice x? Nedefinovali jsme ji v současném rozsahu. Jediným řešením je nechat plus5 carry jeho rozsah (nebo spíše rozsah jeho rodiče) kolem. x je tak dobře definováno a je vázáno na hodnotu 5.

470
Konrad Rudolph

Jedná se o pokus objasnit několik (možných) nedorozumění ohledně uzávěrů, které se objevují v některých dalších odpovědích.

  • Uzavření není vytvořeno pouze při návratu vnitřní funkce. Ve skutečnosti, uzavírací funkce se nemusí vracet vůbec , aby bylo možné uzavřít její uzavření. Namísto toho můžete přiřadit vnitřní funkci proměnné ve vnějším rozsahu, nebo ji předat jako argument jiné funkci, kde by mohla být volána okamžitě nebo kdykoliv později. Uzavření uzavírací funkce je tedy pravděpodobně vytvořeno jakmile se uzavře uzavírací funkce protože každá vnitřní funkce má přístup k tomuto uzavření vždy, když je vyvolána vnitřní funkce, před nebo po návratu uzavírací funkce.
  • Uzavření se nevztahuje na kopii starých hodnot proměnných v jeho rozsahu. Samotné proměnné jsou součástí uzávěrky, takže hodnota, která je viditelná při přístupu k jedné z těchto proměnných, je poslední hodnotou v době, kdy je přistupována. To je důvod, proč vnitřní funkce vytvořené uvnitř smyček mohou být složité, protože každý má přístup ke stejným vnějším proměnným, než aby pořizoval kopii proměnných v době, kdy je funkce vytvořena nebo vyvolána.
  • "Proměnné" v uzávěru zahrnují všechny pojmenované funkce deklarované v rámci funkce. Zahrnují také argumenty funkce. Uzavření má také přístup ke svým proměnným, které obsahují uzávěrku, až do celosvětového rozsahu.
  • Uzávěry používají paměť, ale nezpůsobují úniky paměti protože JavaScript sám o sobě čistí své vlastní kruhové struktury, které nejsou odkazovány. Nevracení paměti aplikace Internet Explorer zahrnující uzávěry se vytvoří, když se nepodaří odpojit hodnoty atributu DOM, které odkazují na uzávěry, a tím zachovat odkazy na případně kruhové struktury.
357
dlaliberte

OK, šestiletý ventilátor. Chcete slyšet nejjednodušší příklad uzavření?

Představme si další situaci: řidič sedí v autě. To auto je uvnitř letadla. Letadlo je na letišti. Schopnost řidiče přistupovat k věci mimo jeho auto, ale uvnitř letadla, i když letadlo opouští letiště, je uzávěr. A je to. Když otočíte 27, podívejte se na podrobnější vysvětlení nebo na příkladu níže.

Zde je návod, jak můžu převést svůj příběh z letadla do kódu.

var plane = function(defaultAirport) {

  var lastAirportLeft = defaultAirport;

  var car = {
    driver: {
      startAccessPlaneInfo: function() {
        setInterval(function() {
          console.log("Last airport was " + lastAirportLeft);
        }, 2000);
      }
    }
  };
  car.driver.startAccessPlaneInfo();

  return {
    leaveTheAirport: function(airPortName) {
      lastAirportLeft = airPortName;
    }
  }
}("Boryspil International Airport");

plane.leaveTheAirport("John F. Kennedy");
356
Max Tkachenko

A uzavření je podobně jako objekt. Dostane instanci, kdykoliv zavoláte funkci.

Rozsah uzavření v JavaScriptu je lexikální, což znamená, že vše, co je obsaženo ve funkci uzavření patří, má přístup k libovolné proměnné, která je v ní obsažena.

Proměnná je obsažena v uzavření pokud jste

  1. přiřaďte ho var foo=1; nebo
  2. stačí napsat var foo;

Pokud vnitřní funkce (funkce obsažená uvnitř jiné funkce) přistupuje k takové proměnné bez jejího definování ve vlastním rozsahu s var, modifikuje obsah proměnné ve vnějším uzavření .

A uzavření přežije běh funkce, která ho vytvořila. Pokud to jiné funkce dělají z uzavření/rozsah ve kterém jsou definovány (například jako návratové hodnoty), tyto budou nadále odkazovat na to, že uzavření .

Příklad

function example(closure) {
  // define somevariable to live in the closure of example
  var somevariable = 'unchanged';

  return {
    change_to: function(value) {
      somevariable = value;
    },
    log: function(value) {
      console.log('somevariable of closure %s is: %s',
        closure, somevariable);
    }
  }
}

closure_one = example('one');
closure_two = example('two');

closure_one.log();
closure_two.log();
closure_one.change_to('some new value');
closure_one.log();
closure_two.log();

Výstup

somevariable of closure one is: unchanged
somevariable of closure two is: unchanged
somevariable of closure one is: some new value
somevariable of closure two is: unchanged
348
Florian Bösch

Napsal jsem blogu, když jsem zpátky vysvětlil uzávěry. Tady je to, co jsem říkal o uzávěrech ve smyslu proč chcete jeden.

Uzávěry jsou způsob, jak nechat funkci mít perzistentní, soukromé proměnné - to znamená proměnné, o nichž ví jen jedna funkce, kde může sledovat informace z předchozích časů, které byly spuštěny.

V tomto smyslu nechají funkci působit trochu jako objekt se soukromými atributy.

Celý příspěvek:

Takže co jsou tyto věci na uzavření?

228
Nathan Long

Uzávěry jsou jednoduché:

Následující jednoduchý příklad pokrývá všechny hlavní body uzavření JavaScriptu.*

Zde je továrna, která vyrábí kalkulačky, které mohou přidávat a násobit:

function make_calculator() {
  var n = 0; // this calculator stores a single number n
  return {
    add: function(a) {
      n += a;
      return n;
    },
    multiply: function(a) {
      n *= a;
      return n;
    }
  };
}

first_calculator = make_calculator();
second_calculator = make_calculator();

first_calculator.add(3); // returns 3
second_calculator.add(400); // returns 400

first_calculator.multiply(11); // returns 33
second_calculator.multiply(10); // returns 4000

Klíčový bod: Každé volání make_calculator vytvoří novou lokální proměnnou n, která je nadále použitelná add a multiply funkcemi kalkulačky dlouho po návratu make_calculator.

Pokud jste obeznámeni se zásobníky rámců, tyto kalkulačky se zdají být podivné: jak mohou přistupovat k n po návratu make_calculator? Odpověď je představit si, že JavaScript nepoužívá "stack frames", ale používá "haldy rámců", které může trvat po volání funkce, která je vrátila.

Vnitřní funkce jako add a multiply, které přistupují k proměnným deklarovaným ve vnější funkci**, se nazývají uzávěry.

To je skoro vše, co je k uzavření.



* Zahrnuje například všechny body v článku "Uzavření pro figuríny" uvedené v další odpověď s výjimkou příkladu 6, který jednoduše ukazuje, že proměnné mohou být použity dříve, než jsou deklarovány, což je pěkný fakt, který má vědět, ale zcela nesouvisející k uzavření. Zahrnuje také všechny body přijatá odpověď s výjimkou bodů (1), které funkce kopírují své argumenty do lokálních proměnných (pojmenované funkční argumenty) a (2) že kopírovací čísla vytvářejí nové číslo, ale kopírování odkazu na objekt vám dává jiný odkaz na stejný objekt. To je také dobré vědět, ale opět zcela nesouvisí s uzavřením. To je také velmi podobné příkladu v tato odpověď ale o něco kratší a méně abstraktní. Nezahrnuje bod tato odpověď nebo tento komentář což je, že JavaScript ztěžuje zapojení aktuálníhodnota proměnné smyčky do vaší vnitřní funkce: „Zapojení "Krok může být proveden pouze s pomocnou funkcí, která uzavírá vaši vnitřní funkci a je vyvolána na každé iteraci smyčky. (Přesně řečeno, vnitřní funkce přistupuje k kopii pomocné funkce proměnné, místo aby měla cokoliv zapojeného.) je užitečný při vytváření uzávěrů, ale není součástí toho, co je uzavření nebo jak funguje.Zavazuje se další zmatek v důsledku uzávěrů, které fungují odlišně ve funkčních jazycích, jako je ML, kde proměnné jsou vázány spíše na hodnoty než na úložný prostor, což poskytuje konstantní proud lidé, kteří rozumějí uzávěry způsobem (konkrétně „připojením“), který je pro JavaScript pouze nesprávný, kde proměnné jsou vždy vázány na paměťový prostor a nikdy na hodnoty.

** Jakákoliv vnější funkce, pokud je několik vnořených, nebo dokonce v globálním kontextu, jako tato odpověď jasně poukazuje.

208
Matt

Jak bych to vysvětlil šestiletému:

Víte, jak dospělí mohou vlastnit dům, a říkají tomu domů? Když máma má dítě, dítě vlastně nic nevlastní, že? Ale jeho rodiče vlastní dům, takže kdykoli se někdo zeptá dítěte „Kde je váš domov?“, Může odpovědět „ten dům!“ A ukázat na dům rodičů. "Uzavření" je schopnost dítěte vždy (i když v zahraničí) říci, že má doma, i když je to vlastně rodiče, kteří vlastní dům.

203
Magne

Dokážete vysvětlit uzavření 5letému věku? *

Stále si myslím vysvětlení společnosti Google funguje velmi dobře a je stručné:

/*
*    When a function is defined in another function and it
*    has access to the outer function's context even after
*    the outer function returns.
*
* An important concept to learn in JavaScript.
*/

function outerFunction(someNum) {
    var someString = 'Hey!';
    var content = document.getElementById('content');
    function innerFunction() {
        content.innerHTML = someNum + ': ' + someString;
        content = null; // Internet Explorer memory leak for DOM reference
    }
    innerFunction();
}

outerFunction(1);​

Proof that this example creates a closure even if the inner function doesn't return

* Otázka C #

194
Chris S

Mám tendenci se lépe učit pomocí GOOD/BAD srovnání. Rád vidím pracovní kód následovaný nepracovním kódem, se kterým se někdo pravděpodobně setká. Sestavil jsem jsFiddle který dělá srovnání a pokouší se zredukovat rozdíly na nejjednodušší vysvětlení, s nimiž jsem mohl přijít.

Uzávěry byly provedeny správně:

console.log('CLOSURES DONE RIGHT');

var arr = [];

function createClosure(n) {
    return function () {
        return 'n = ' + n;
    }
}

for (var index = 0; index < 10; index++) {
    arr[index] = createClosure(index);
}

for (var index in arr) {
    console.log(arr[index]());
}
  • Ve výše uvedeném kódu je createClosure(n) vyvolán v každé iteraci smyčky. Všimněte si, že jsem proměnnou n pojmenoval, abych zdůraznil, že se jedná o proměnnou new vytvořenou v novém rozsahu funkcí a není stejná proměnná jako index, která je vázána na vnější rozsah.

  • To vytvoří nový rozsah a n je vázán na tento rozsah; to znamená, že máme 10 samostatných oborů, jeden pro každou iteraci.

  • createClosure(n) vrátí funkci, která vrátí n v rámci tohoto rozsahu.

  • V rámci každého rozsahu je n vázán na jakoukoli hodnotu, kterou měl, když byla createClosure(n) vyvolána, takže vnořená funkce, která se vrátí, vždy vrátí hodnotu n, kterou měla při vyvolání createClosure(n).

Uzávěry byly špatné:

console.log('CLOSURES DONE WRONG');

function createClosureArray() {
    var badArr = [];

    for (var index = 0; index < 10; index++) {
        badArr[index] = function () {
            return 'n = ' + index;
        };
    }
    return badArr;
}

var badArr = createClosureArray();

for (var index in badArr) {
    console.log(badArr[index]());
}
  • Ve výše uvedeném kódu byla smyčka přesunuta v rámci funkce createClosureArray() a funkce nyní vrací pouze dokončené pole, které se na první pohled zdá intuitivnější.

  • Co nemusí být zřejmé, je to, že protože createClosureArray() je vyvolána pouze jednou, je pro tuto funkci vytvořen pouze jeden rozsah namísto jednoho pro každou iteraci smyčky.

  • V rámci této funkce je definována proměnná s názvem index. Smyčka běží a přidává funkce do pole, které vrací index. Všimněte si, že index je definováno v rámci createClosureArray funkce, která je vyvolána pouze jednou.

  • Protože v rámci funkce createClosureArray() existovala pouze jedna oblast, index je vázána pouze na hodnotu v rámci tohoto rozsahu. Jinými slovy, pokaždé, když smyčka změní hodnotu index, změní ji pro vše, co ji v tomto rozsahu odkazuje.

  • Všechny funkce přidané do pole vrátí proměnnou SAME index z rodičovského oboru, kde byla definována namísto 10 různých z 10 různých oborů, jako je první příklad. Konečným výsledkem je, že všech 10 funkcí vrátí stejnou proměnnou ze stejného rozsahu.

  • Po dokončení smyčky a provedení index bylo změněno koncová hodnota 10, proto každá funkce přidaná do pole vrátí hodnotu jediné proměnné index, která je nyní nastavena na hodnotu 10.

Výsledek

ZAVŘÍT PRAVDĚ
n = 0
[.] n = 1
[.] n = 2
[.] n = 3
[.] n = 4
[.] n = 5
[.] n = 6
[.] n = 7
[.] n = 8
[.] n = 9

ZAVŘÍT ZAVŘÍT
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10
[.] n = 10

168
Chev

Wikipedia o uzavření :

V počítačové vědě je uzávěr funkcí spolu s referenčním prostředím pro nelokální názvy (volné proměnné) této funkce.

Technicky, v JavaScript , každá funkce je uzávěr . Má vždy přístup k proměnným definovaným v okolním rozsahu.

Protože konstrukce definující rozsah v JavaScriptu je funkce , není kódový blok jako v mnoha jiných jazycích, co obvykle znamená uzavření v JavaScriptu je a funkce pracující s nelokálními proměnnými definovanými v již provedené okolní funkci .

Uzávěry jsou často používány pro vytváření funkcí s některými skrytými soukromými daty (ale není to vždy tak).

var db = (function() {
    // Create a hidden object, which will hold the data
    // it's inaccessible from the outside.
    var data = {};

    // Make a function, which will provide some access to the data.
    return function(key, val) {
        if (val === undefined) { return data[key] } // Get
        else { return data[key] = val } // Set
    }
    // We are calling the anonymous surrounding function,
    // returning the above inner function, which is a closure.
})();

db('x')    // -> undefined
db('x', 1) // Set x to 1
db('x')    // -> 1
// It's impossible to access the data object itself.
// We are able to get or set individual it.

ems

Výše uvedený příklad používá anonymní funkci, která byla provedena jednou. Ale nemusí to být. Může být pojmenován (např. mkdb) a proveden později, při každém vyvolání funkce databáze. Každá vygenerovaná funkce bude mít svůj vlastní skrytý databázový objekt. Dalším příkladem použití uzávěrů je, když nevracíme funkci, ale objekt obsahující více funkcí pro různé účely, přičemž každá z těchto funkcí má přístup ke stejným datům.

161
mykhal

Vytvořil jsem interaktivní výukový program JavaScript, který vysvětluje, jak fungují uzávěry. Co je to uzavření?

Zde je jeden z příkladů:

var create = function (x) {
    var f = function () {
        return x; // We can refer to x here!
    };
    return f;
};
// 'create' takes one argument, creates a function

var g = create(42);
// g is a function that takes no arguments now

var y = g();
// y is 42 here
135
Nathan Whitehead

Děti si budou vždy pamatovat tajemství, které sdílejí se svými rodiči, a to i poté, co jsou jejich rodiče pryč. To je to, co jsou uzávěry pro funkce.

Tajemství funkcí JavaScriptu jsou soukromé proměnné

var parent = function() {
 var name = "Mary"; // secret
}

Pokaždé, když to nazvete, vytvoří se lokální proměnná "name" a jméno "Mary". A pokaždé, když se funkce ukončí, je proměnná ztracena a jméno je zapomenuto.

Jak možná hádáte, protože proměnné jsou znovu vytvářeny pokaždé, když je funkce volána, a nikdo jiný ji nezná, musí být tam tajné místo, kde jsou uloženy. To by mohlo být nazýváno Komorou tajemství nebo stackem nebo lokálním rozsahem ale na tom nezáleží. Víme, že jsou někde někde schovaní v paměti.

Ale v JavaScriptu je tato velmi zvláštní věc, že ​​funkce, které jsou vytvořeny uvnitř jiných funkcí, mohou také znát lokální proměnné svých rodičů a udržet je tak dlouho, dokud žijí.

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    // I can also see that "name" is "Mary"
  }
}

Takže pokud jsme v rodičovské funkci, může vytvořit jednu nebo více podřízených funkcí, které sdílejí tajné proměnné z tajného místa.

Ale smutné je, že pokud je dítě také soukromou proměnnou své mateřské funkce, zemřelo by to i tehdy, když rodič skončí a tajemství s nimi zemře.

Aby žilo, dítě musí odejít, než bude pozdě

var parent = function() {
  var name = "Mary";
  var child = function(childName) {
    return "My name is " + childName  +", child of " + name; 
  }
  return child; // child leaves the parent ->
}
var child = parent(); // < - and here it is outside 

A teď, i když je Mary "již neběží", její paměť není ztracena a její dítě si bude vždy pamatovat její jméno a další tajemství, která sdílejí během svého času.

Pokud tedy zavoláte dítě "Alice", odpoví

child("Alice") => "My name is Alice, child of Mary"

To je vše, co je třeba říct.

124
Tero Tolonen

Nechápu, proč jsou odpovědi tak složité.

Zde je závěr:

var a = 42;

function b() { return a; }

Ano. Pravděpodobně používáte mnohokrát denně.


Není důvod se domnívat, že uzávěry jsou komplexním designovým hackem pro řešení konkrétních problémů. Ne, uzávěry jsou jen o použití proměnné, která pochází z vyššího rozsahu z pohledu, kde byla funkce deklarována (neběží) .

Co to umožňuje můžete dělat, může být více okázalý, viz další odpovědi.

103
floribon

Příklad prvního bodu podle dlaliberte:

Uzavření není vytvořeno pouze při návratu vnitřní funkce. Ve skutečnosti se uzavírací funkce nemusí vůbec vracet. Namísto toho můžete přiřadit vnitřní funkci proměnné ve vnějším rozsahu, nebo ji předat jako argument jiné funkci, kde by mohla být použita okamžitě. Proto uzavření uzavírací funkce pravděpodobně již existuje v době, kdy byla zavolána uzavírací funkce, protože každá vnitřní funkce k ní má přístup, jakmile je zavolána.

var i;
function foo(x) {
    var tmp = 3;
    i = function (y) {
        console.log(x + y + (++tmp));
    }
}
foo(2);
i(3);
91
someisaac

Uzavření je tam, kde vnitřní funkce má přístup k proměnným ve své vnější funkci. To je asi nejjednodušší jednořádkové vysvětlení, které můžete získat pro uzavření.

86
Rakesh Pai

Vím, že již existuje spousta řešení, ale myslím, že tento malý a jednoduchý skript může být užitečný k prokázání konceptu:

// makeSequencer will return a "sequencer" function
var makeSequencer = function() {
    var _count = 0; // not accessible outside this function
    var sequencer = function () {
        return _count++;
    }
    return sequencer;
}

var fnext = makeSequencer();
var v0 = fnext();     // v0 = 0;
var v1 = fnext();     // v1 = 1;
var vz = fnext._count // vz = undefined
84
Gerardo Lima

Přespáváš a pozveš Dana. Řeknete Danovi, aby přinesl jeden řadič XBox.

Dan zve Pavla. Dan požádá Paula, aby přinesl jednoho regulátora. Kolik řadičů bylo přineseno na večírek?

function sleepOver(howManyControllersToBring) {

    var numberOfDansControllers = howManyControllersToBring;

    return function danInvitedPaul(numberOfPaulsControllers) {
        var totalControllers = numberOfDansControllers + numberOfPaulsControllers;
        return totalControllers;
    }
}

var howManyControllersToBring = 1;

var inviteDan = sleepOver(howManyControllersToBring);

// The only reason Paul was invited is because Dan was invited. 
// So we set Paul's invitation = Dan's invitation.

var danInvitedPaul = inviteDan(howManyControllersToBring);

alert("There were " + danInvitedPaul + " controllers brought to the party.");
81
StewShack

Funkce JavaScript mohou přistupovat k:

  1. Argumenty
  2. Místní (tj. Jejich lokální proměnné a lokální funkce)
  3. Prostředí, které zahrnuje:
    • globálů, včetně DOM
    • nic ve vnějších funkcích

Pokud funkce přistupuje do svého prostředí, pak je funkcí uzavření.

Všimněte si, že vnější funkce nejsou nutné, i když nabízejí výhody, o kterých zde nemluvím. Přístupem k datům ve svém prostředí uzávěr udržuje tato data živá. V subcase vnějších/vnitřních funkcí, vnější funkce může vytvořit lokální data a nakonec vystoupit, a přesto, jestliže nějaká vnitřní funkce (s) přežít poté, co vnější funkce skončí, pak vnitřní funkce (funkce) držet vnější funkce je místní data\t naživu.

Příklad uzavření, které používá globální prostředí:

Představte si, že události hromadného přetečení hlasování a hlasování dolů jsou implementovány jako uzávěry, voiceUp_click a voteDown_click, které mají přístup k externím proměnným isVotedUp a isVotedDown, které jsou definovány globálně. (Pro zjednodušení se odvolávám na tlačítka Hlasování o otázkách typu StackOverflow, nikoli pole tlačítek Hlasování odpovědí.)

Když uživatel klikne na tlačítko VoteUp, funkce votingUp_click zkontroluje, zda isVotedDown == true určuje, zda se má hlasovat nahoru nebo pouze zrušit hlasování dolů. Funkce votingUp_click je uzavření, protože přistupuje k prostředí.

var isVotedUp = false;
var isVotedDown = false;

function voteUp_click() {
  if (isVotedUp)
    return;
  else if (isVotedDown)
    SetDownVote(false);
  else
    SetUpVote(true);
}

function voteDown_click() {
  if (isVotedDown)
    return;
  else if (isVotedUp)
    SetUpVote(false);
  else
    SetDownVote(true);
}

function SetUpVote(status) {
  isVotedUp = status;
  // Do some CSS stuff to Vote-Up button
}

function SetDownVote(status) {
  isVotedDown = status;
  // Do some CSS stuff to Vote-Down button
}

Všechny tyto funkce jsou uzávěry, protože mají přístup ke svému prostředí.

77
John Pick

Autor Closures vysvětlil uzávěry docela dobře, vysvětlil důvod, proč je potřebujeme, a také vysvětlil LexicalEnvironment, který je nezbytný pro pochopení uzávěrů.
Zde je shrnutí:

Co když je přístupná proměnná, ale není místní? Jako tady:

 Enter image description here

V tomto případě interpret nalezne proměnnou ve vnějším LexicalEnvironment object.

Proces se skládá ze dvou kroků:

  1. Za prvé, když je vytvořena funkce f, není vytvořena v prázdném prostoru. Existuje aktuální objekt LexicalEnvironment. V tomto případě je to okno (a je nedefinováno v okamžiku vytvoření funkce).

 Enter image description here

Když je funkce vytvořena, dostane skrytou vlastnost s názvem [[Scope]], která odkazuje na aktuální LexicalEnvironment.

 Enter image description here

Pokud je proměnná čtena, ale nelze ji nikde nalézt, vygeneruje se chyba.

Vnořené funkce

Funkce mohou být vnořeny jeden do druhého, což vytváří řetězec LexicalEnvironments, který lze také nazvat řetězcem rozsahu.

 Enter image description here

Funkce g má tedy přístup k g, a a f.

Uzavření

Vnořená funkce může nadále žít po dokončení vnější funkce:

 Enter image description here

Označení Lexikální prostředí:

 Enter image description here

Jak vidíme, this.say je vlastnost v uživatelském objektu, takže pokračuje v životě po dokončení Uživatele.

A pokud si vzpomenete, kdy je this.say vytvořeno, dostane (jako každá funkce) interní odkaz this.say.[[Scope]] do aktuálního LexicalEnvironment. LexicalEnvironment aktuálního spuštění uživatele tedy zůstává v paměti. Všechny proměnné Uživatele jsou také jeho vlastnostmi, takže jsou také pečlivě uchovávány, nikoliv jako obvykle.

Celým bodem je zajistit, že pokud chce vnitřní funkce v budoucnu přistupovat k vnější proměnné, je schopna tak učinit.

Shrnout:

  1. Vnitřní funkce udržuje odkaz na vnější LexicalEnvironment.
  2. Vnitřní funkce může mít přístup k proměnným z ní kdykoliv, i když je vnější funkce dokončena.
  3. Prohlížeč udržuje LexicalEnvironment a všechny jeho vlastnosti (proměnné) v paměti, dokud neexistuje vnitřní funkce, která by ho odkazovala.

Toto se nazývá uzavření.

75
Arvand

Jako otec šestiletého dítěte, který v současné době vyučuje malé děti (a relativní začátečníky v oblasti kódování bez formálního vzdělání, které bude vyžadovat opravy), se domnívám, že lekce by se měla nejlépe uplatnit prostřednictvím praktické hry. Pokud je šestiletý člověk připraven pochopit, co je to uzavření, pak jsou dost staří na to, aby si mohli jít sami. Navrhoval bych vložit kód do jsfiddle.net, trochu vysvětlit a nechat je, aby si vymysleli jedinečnou píseň. Níže uvedený vysvětlující text je pravděpodobně vhodnější pro desetiletého věku.

function sing(person) {

    var firstPart = "There was " + person + " who swallowed ";

    var fly = function() {
        var creature = "a fly";
        var result = "Perhaps she'll die";
        alert(firstPart + creature + "\n" + result);
    };

    var spider = function() {
        var creature = "a spider";
        var result = "that wiggled and jiggled and tickled inside her";
        alert(firstPart + creature + "\n" + result);
    };

    var bird = function() {
        var creature = "a bird";
        var result = "How absurd!";
        alert(firstPart + creature + "\n" + result);
    };

    var cat = function() {
        var creature = "a cat";
        var result = "Imagine That!";
        alert(firstPart + creature + "\n" + result);
    };

    fly();
    spider();
    bird();
    cat();
}

var person="an old lady";

sing(person);

NÁVOD

ÚDAJE: Data jsou souborem faktů. Může to být čísla, slova, měření, pozorování nebo dokonce jen popisy věcí. Nemůžete se ho dotknout, cítit nebo ochutnat. Můžete to napsat, mluvit a slyšet. Dalo by se použít vytvořit dotek a chuť pomocí počítače. To může být užitečné pro počítač pomocí kódu.

KÓD: Veškeré výše uvedené písmo se nazývá kód . Je napsán v jazyce JavaScript.

JAVASCRIPT: JavaScript je jazyk. Jako angličtina nebo francouzština nebo čínština jsou jazyky. Existuje mnoho jazyků, kterým rozumějí počítače a další elektronické procesory. Pro pochopení JavaScriptu počítačem potřebuje tlumočníka. Představte si, že učitel, který mluví pouze rusky, přijde učit vaši třídu ve škole. Když učitel řekne "все садятся", třída by tomu nerozuměla. Ale naštěstí máte ve své třídě ruského žáka, který každému říká, že to znamená "všichni si sednou" - takže to všichni děláte. Třída je jako počítač a ruský žák je tlumočník. Pro JavaScript je nejběžnějším interpretem prohlížeč.

PROHLÍŽEČ: Když se připojujete k Internetu v počítači, tabletu nebo telefonu a navštívíte web, použijte prohlížeč. Příklady, které možná víte, jsou Internet Explorer, Chrome, Firefox a Safari. Prohlížeč může pochopit JavaScript a říct počítači, co musí udělat. Instrukce JavaScriptu se nazývají funkce.

FUNKCE: Funkce v JavaScriptu je jako továrna. Může to být malá továrna s pouze jedním strojem uvnitř. Nebo může obsahovat mnoho dalších malých továren, z nichž každá má mnoho strojů, které pracují různě. V továrně na opravdový oděv můžete mít na sobě šňůry látky a cívek nití a trička a džíny. Naše továrna JavaScript zpracovává pouze data, nemůže šít, vrtat díru nebo roztavit kov. V našem JavaScriptu jde o tovární data a data vycházejí.

Všechny tyto údaje znějí trochu nudně, ale je to opravdu velmi cool; mohli bychom mít funkci, která říká robotovi, co má dělat na večeři. Řekněme, že vás a svého přítele pozývám do mého domu. Líbí se vám nejlepší kuřecí stehýnka, mám rád klobásy, váš přítel vždy chce to, co chcete, a můj přítel nejí maso.

Nemám čas jít nakupovat, takže funkce musí vědět, co máme v lednici, abychom se rozhodli. Každá složka má různou dobu vaření a chceme, aby vše bylo obsluhováno robotem současně. Potřebujeme poskytnout funkci s údaji o tom, co se nám líbí, funkce by mohla 'mluvit' s lednicí a funkce by mohla ovládat robota.

Funkce má obvykle název, závorky a závorky. Takhle:

function cookMeal() {  /*  STUFF INSIDE THE FUNCTION  */  }

Všimněte si, že /*...*/ a // stop kód je čten prohlížečem

JMÉNO: Funkci můžete zavolat cokoliv, co chcete. Příklad "cookMeal" je typický pro spojení dvou slov dohromady a dávání druhého velkého písmene na začátku - ale to není nutné. Nemůže v něm mít prostor a nemůže to být číslo samotné.

RODIČE: "Závorky" nebo () jsou poštovní schránka na dveřích továrny JavaScriptu nebo poštovní schránka na ulici pro zasílání paketů informací do továrny. Někdy může být poštovní schránka označena například cookMeal(you, me, yourFriend, myFriend, fridge, dinnerTime), v takovém případě víte, jaká data musíte zadat.

BRACES: "Braces", které vypadají jako {} jsou tónovaná okna naší továrny. Zevnitř továrny můžete vidět ven, ale zvenčí nevidíte.

PŘÍKLAD DLOUHÉHO KÓDU NADE

Náš kód začíná funkcí Word , takže víme, že je jedna! Pak název funkce zpívat - to je můj vlastní popis toho, o čem je funkce. Pak závorky () . Závorky jsou zde vždy pro funkci. Někdy jsou prázdné a někdy mají něco. Ten má slovo v: (person). Po tomto je ortéza jako tento {. To označuje začátek funkce zpívat () . Má partnera, který označuje konec zpěvu () jako tento }

function sing(person) {  /* STUFF INSIDE THE FUNCTION */  }

Tato funkce by tedy mohla mít něco společného se zpěvem a mohla by potřebovat nějaká data o osobě. Má instrukce uvnitř, aby s těmito daty něco udělal.

Nyní, po funkci zpívat () , poblíž konce kódu je řádek

var person="an old lady";

VARIABLE: Písmena var znamenají "proměnnou". Proměnná je jako obálka. Na vnější straně je tato obálka označena "osoba". Vevnitř obsahuje skluz papíru s informacemi, které naše funkce potřebuje, některá písmena a mezery se spojují jako kousek řetězce (to se nazývá řetězec), které tvoří frázi, která říká "stará dáma". Naše obálka by mohla obsahovat další druhy věcí, jako jsou čísla (nazývaná celá čísla), instrukce (nazývané funkce), seznamy (nazývané pole ). Protože tato proměnná je zapsána mimo všechny závorky {} a protože můžete vidět přes tónovaná okna, když jste uvnitř závorek, tato proměnná může být viděna odkudkoli v kódu. Nazýváme to „globální proměnnou“.

GLOBÁLNÍ VARIABLE: osoba je globální proměnná, což znamená, že pokud změníte její hodnotu z „staré dámy“ na „mladého muže“, osoba bude i nadále mladým mužem, dokud se nezměníte znovu a že jakákoli jiná funkce v kódu může vidět, že je to mladý muž. zmáčkni F12 Chcete-li otevřít konzolu pro vývojáře prohlížeče a zadat hodnotu „osoba“, klepněte na tlačítko Možnosti nebo na položku Možnosti. Zadejte person="a young man" jej změňte a pak znovu zadejte "person", abyste zjistili, že se změnila.

Po tomto máme linku

sing(person);

Tato linka volá funkci, jako by volala psa

"Pojď zpívat , Přijď a dostat osobu !"

Když prohlížeč načte tento kód JavaScriptu, spustí tuto funkci. Dal jsem řádek na konci, aby se ujistil, že prohlížeč má všechny informace, které potřebuje ke spuštění.

Funkce definují akce - hlavní funkcí je zpěv. Obsahuje proměnnou nazvanou firstPart , která se týká zpěvu o osobě, která se vztahuje na každý z veršů písně: "Tam byl" + osoba + "kdo spolkl". Pokud do konzoly zadáte firstPart , nedostanete odpověď, protože proměnná je uzamčena ve funkci - prohlížeč nevidí uvnitř tónovaná okna šle.

ZÁVĚRY: Uzávěry jsou menší funkce, které jsou uvnitř velké funkce sing () . Malé továrny uvnitř velké továrny. Každý z nich má své rovnátka, což znamená, že proměnné v nich nelze vidět zvenčí. Proto mohou být názvy proměnných ( tvor a výsledek ) opakuje se v uzávěrech, ale s různými hodnotami. Pokud tyto názvy proměnných zadáte do okna konzoly, nedostanete jejich hodnotu, protože je skryta dvěma vrstvami tónovaných oken.

Uzávěry všechny vědí, co proměnná funkce nazvaná firstPart je , protože mohou vidět ze svých tónovaných oken.

Po uzávěrech přicházejí linky

fly();
spider();
bird();
cat();

Funkce sing () bude volat každou z těchto funkcí v pořadí, v jakém jsou uvedeny. Pak bude provedena funkce funkce sing ().

59
grateful

Okay, mluvil jsem s 6letým dítětem, možná bych použil následující asociace.

Představte si - hrajete se svými malými bratry a sestrami v celém domě a pohybujete se s hračkami a přivezete je do pokoje svého staršího bratra. Po chvíli se váš bratr vrátil ze školy a šel do svého pokoje, a zamkl uvnitř, takže teď už nemůžete mít přístup k hračkám, které tam zůstaly přímým způsobem. Ale mohl byste srazit dveře a požádat svého bratra o ty hračky. Toto se nazývá hračka uzavření ; Tvůj bratr to pro tebe vymyslel a on je nyní ve vnějším rozsahu .

Porovnejte se situací, kdy byly dveře uzamčeny tahem a nikdo uvnitř (provedení obecné funkce), a pak došlo k nějakému lokálnímu požáru a spálili místnost (sběrač odpadu: D), a pak byl postaven nový pokoj a teď můžete odejít. další hračky tam (nová funkce instance), ale nikdy dostat stejné hračky, které byly ponechány v první instanci místnosti.

Pro pokročilé dítě bych dal něco takového. Není to dokonalé, ale máte pocit, že to je:

function playingInBrothersRoom (withToys) {
  // We closure toys which we played in the brother's room. When he come back and lock the door
  // your brother is supposed to be into the outer [[scope]] object now. Thanks god you could communicate with him.
  var closureToys = withToys || [],
      returnToy, countIt, toy; // Just another closure helpers, for brother's inner use.

  var brotherGivesToyBack = function (toy) {
    // New request. There is not yet closureToys on brother's hand yet. Give him a time.
    returnToy = null;
    if (toy && closureToys.length > 0) { // If we ask for a specific toy, the brother is going to search for it.

      for ( countIt = closureToys.length; countIt; countIt--) {
        if (closureToys[countIt - 1] == toy) {
          returnToy = 'Take your ' + closureToys.splice(countIt - 1, 1) + ', little boy!';
          break;
        }
      }
      returnToy = returnToy || 'Hey, I could not find any ' + toy + ' here. Look for it in another room.';
    }
    else if (closureToys.length > 0) { // Otherwise, just give back everything he has in the room.
      returnToy = 'Behold! ' + closureToys.join(', ') + '.';
      closureToys = [];
    }
    else {
      returnToy = 'Hey, lil shrimp, I gave you everything!';
    }
    console.log(returnToy);
  }
  return brotherGivesToyBack;
}
// You are playing in the house, including the brother's room.
var toys = ['teddybear', 'car', 'jumpingrope'],
    askBrotherForClosuredToy = playingInBrothersRoom(toys);

// The door is locked, and the brother came from the school. You could not cheat and take it out directly.
console.log(askBrotherForClosuredToy.closureToys); // Undefined

// But you could ask your brother politely, to give it back.
askBrotherForClosuredToy('teddybear'); // Hooray, here it is, teddybear
askBrotherForClosuredToy('ball'); // The brother would not be able to find it.
askBrotherForClosuredToy(); // The brother gives you all the rest
askBrotherForClosuredToy(); // Nothing left in there

Jak vidíte, hračky ponechané v místnosti jsou stále přístupné přes bratra a bez ohledu na to, zda je místnost zamčená. Zde je a jsbin hrát si s ním.

55
dmi3y

Odpověď pro šestiletého (za předpokladu, že ví, jaká funkce je a jaká proměnná je a jaká data jsou):

Funkce mohou vrátit data. Jeden druh dat, které lze z funkce vrátit, je další funkce. Když se tato nová funkce vrátí, všechny proměnné a argumenty použité ve funkci, která ji vytvořila, nezmizí. Místo toho tato rodičovská funkce "zavře." Jinými slovy, uvnitř se nic nemůže podívat a vidět proměnné, které použila, s výjimkou funkce, kterou vrátila. Tato nová funkce má zvláštní schopnost ohlédnout se zpět do funkce, která ji vytvořila, a vidět data uvnitř ní.

function the_closure() {
  var x = 4;
  return function () {
    return x; // Here, we look back inside the_closure for the value of x
  }
}

var myFn = the_closure();
myFn(); //=> 4

Dalším opravdu jednoduchým způsobem, jak to vysvětlit, je rozsah:

Kdykoliv vytvoříte menší rozsah uvnitř většího rozsahu, menší rozsah bude vždy schopen vidět, co je ve větším rozsahu.

50
Stupid Stupid

Funkce v JavaScriptu není jen odkazem na sadu instrukcí (jako v jazyce C), ale také obsahuje skrytou datovou strukturu, která se skládá z odkazů na všechny nelokální proměnné, které používá (zachycené proměnné). Takové dvoudílné funkce se nazývají uzávěry. Každá funkce v JavaScriptu může být považována za uzavření.

Uzávěry jsou funkce se stavem. To je poněkud podobné “toto” v tom smyslu, že “toto” také poskytuje stav pro funkci ale funkce a “toto” jsou oddělené objekty (“toto” je jen fancy parametr, a jediný způsob, jak svázat to trvale k funkcí je vytvořit uzávěr). Zatímco "toto" a funkce vždy žijí odděleně, funkce nemůže být oddělena od jejího uzavření a jazyk neposkytuje žádné prostředky pro přístup k zachyceným proměnným.

Protože všechny tyto externí proměnné, na které odkazuje lexikálně vnořená funkce, jsou vlastně lokální proměnné v řetězci jeho lexikálně uzavírajících funkcí (globální proměnné lze považovat za lokální proměnné některé kořenové funkce) a každé jednotlivé provedení funkce vytvoří nové instance jeho lokální proměnné vyplývá, že každé provedení funkce, která se vrací (nebo jej jinak přenáší, jako je např. registrace jako zpětné volání) vnořenou funkci, vytvoří nový uzávěr (s vlastním potenciálně jedinečným souborem odkazovaných nelokálních proměnných, které představují jeho provedení). kontext).

Také je třeba chápat, že lokální proměnné v JavaScriptu nejsou vytvořeny na rámu zásobníku, ale na haldě a jsou zničeny pouze tehdy, když je nikdo neodkazuje. Když se funkce vrátí, odkazy na její lokální proměnné se sníží, ale mohou být stále nenulové, pokud se během aktuálního provedení stanou součástí uzávěrky a jsou stále odkazovány svými lexikálně vnořenými funkcemi (což se může stát pouze v případě, že odkazy na tyto vnořené funkce byly vráceny nebo jinak přeneseny do nějakého externího kódu).

Příklad:

function foo (initValue) {
   //This variable is not destroyed when the foo function exits.
   //It is 'captured' by the two nested functions returned below.
   var value = initValue;

   //Note that the two returned functions are created right now.
   //If the foo function is called again, it will return
   //new functions referencing a different 'value' variable.
   return {
       getValue: function () { return value; },
       setValue: function (newValue) { value = newValue; }
   }
}

function bar () {
    //foo sets its local variable 'value' to 5 and returns an object with
    //two functions still referencing that local variable
    var obj = foo(5);

    //Extracting functions just to show that no 'this' is involved here
    var getValue = obj.getValue;
    var setValue = obj.setValue;

    alert(getValue()); //Displays 5
    setValue(10);
    alert(getValue()); //Displays 10

    //At this point getValue and setValue functions are destroyed
    //(in reality they are destroyed at the next iteration of the garbage collector).
    //The local variable 'value' in the foo is no longer referenced by
    //anything and is destroyed too.
}

bar();
49
srgstm

Snad o něco více než všechno, co je předčasně šestileté, ale pár příkladů, které mi pomohly vytvořit koncept uzavření v JavaScriptu.

Uzavření je funkce, která má přístup k rozsahu jiné funkce (jeho proměnné a funkce). Nejjednodušší způsob, jak vytvořit uzávěr, je funkce v rámci funkce; důvodem je to, že v JavaScriptu má funkce vždy přístup k rozsahu své funkce.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    innerFunction();
}

outerFunction();

ALERT: opice

Ve výše uvedeném příkladu se nazývá externalFunction, který zase volá innerFunction. Všimněte si, že externalVar je k dispozici pro innerFunction, což dokazuje jeho správné varování hodnoty externalVar.

Zvažte následující:

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

ALERT: opice

referenceToInnerFunction je nastavena na externalFunction (), která jednoduše vrací odkaz na innerFunction. Když se nazývá referenceToInnerFunction, vrátí vnější hodnotu. Opět, jak je uvedeno výše, to ukazuje, že innerFunction má přístup k vnějšímuVar, proměnné externalFunction. Dále je zajímavé poznamenat, že si tento přístup zachovává i po dokončení vnějšíhoFunkce.

A tady jsou věci, které jsou opravdu zajímavé. Kdybychom se měli zbavit vnějších funkcí, řekněme, že by to mělo být nulové, můžete si myslet, že referenceToInnerFunction by ztratil svůj přístup k hodnotě externalVar. Ale to tak není.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        return outerVar;
    }
    
    return innerFunction;
}

var referenceToInnerFunction = outerFunction();
alert(referenceToInnerFunction());

outerFunction = null;
alert(referenceToInnerFunction());

ALERT: opice ALERT: opice

Ale jak je to tak? Jak může referenceToInnerFunction ještě znát hodnotu externalVar, že vnější funkce byla nastavena na hodnotu null?

Důvodem, proč referenceToInnerFunction může stále přistupovat k hodnotě vnějšku, je to, že když byl uzávěr poprvé vytvořen umístěním vnitřní funkce uvnitř vnějšíhofunkce, vnitřní funkce přidala odkaz na rozsah vnějších funkcí (jeho proměnné a funkce) do řetězce jeho rozsahu. To znamená, že vnitřní funkce má ukazatel nebo odkaz na všechny proměnné vnějších funkcí, včetně vnějších proměnných. Takže i když externalFunction dokončil provádění, nebo i když je smazán nebo nastaven na null, proměnné v jeho rozsahu, jako je vnější, se v paměti drží kvůli vynikajícímu odkazu na část vnitřní funkce, která byla vrácena. referenceToInnerFunction. Chcete-li skutečně uvolnit vnější proměnnou zevnitř a zbytek proměnných vnějších funkcí z paměti, musíte se jich zbavit tohoto mimořádného odkazu, například nastavením referenceToInnerFunction na hodnotu null.

//////////

Dvě další věci o uzavření. Zaprvé, uzávěr bude mít vždy přístup k posledním hodnotám své funkce.

function outerFunction() {
    var outerVar = "monkey";
    
    function innerFunction() {
        alert(outerVar);
    }
    
    outerVar = "gorilla";

    innerFunction();
}

outerFunction();

ALERT: gorila

Za druhé, když je vytvořen uzávěr, zachovává si odkaz na všechny jeho proměnné a funkce; není to výběr a výběr. Uzávěry by však měly být používány šetrně nebo alespoň pečlivě, protože mohou být paměťově náročné; spousta proměnných může být uchovávána v paměti dlouho po dokončení funkce obsahující.

48

Jednoduše bych je ukázal na stránku Mozilla Closures . Je to to nejlepší, nejvíce stručné a jednoduché vysvětlenízáklady uzávěrky a praktické využití, které jsem našel. Doporučuje se každému, kdo se učí JavaScript.

A ano, dokonce bych to doporučil 6letému věku - pokud se 6letý člověk učí o uzavření, pak je logické, že jsou připraveni pochopit stručné a jednoduché vysvětlení článek.

45
mjmoody383

Věřím v kratší vysvětlení, takže viz obrázek níže.

Enter image description here

function f1() ..> Světle červený rámeček

function f2() ..> Červená malá schránka

Zde máme dvě funkce, f1() a f2(). f2 () je vnitřní až f1 (). f1 () má proměnnou var x = 10.

Při vyvolání funkce f1() může f2() přistupovat k hodnotě var x = 10.

Zde je kód:

function f1() {
    var x=10;

    function f2() {
        console.log(x)
    }

    return f2

}
f1()

f1() vyvolání zde:

Enter image description here

42
Dinesh Kanivu

Uzavření je funkce, která má přístup k nadřazenému rozsahu i po zavření rodičovské funkce.

Uzavření je tedy funkcí jiné funkce. Můžeme říci jako dětská funkce.

Uzavření je vnitřní funkce, která má přístup k proměnným vnější (obklopující) funkce - řetězci rozsahu. Uzávěr má tři řetězy rozsahu: má přístup ke svému vlastnímu rozsahu (proměnné definované mezi jeho složenými závorkami), má přístup k proměnným vnější funkce a má přístup ke globálním proměnným.

Vnitřní funkce má přístup nejen k proměnným vnější funkce, ale také k parametrům vnější funkce. Všimněte si, že vnitřní funkce nemůže volat objekt argumentu vnější funkce, i když může přímo volat parametry vnější funkce.

Uzavření vytvoříte přidáním funkce do jiné funkce.

Také je to velmi užitečná metoda, která se používá v mnoha slavných rámcích, včetně Angular, Node.js a jQuery:

Uzávěry jsou používány značně v Node.js; jsou to pracovní koně v asynchronní, neblokující architektuře Node.js. Uzávěry jsou také často používány v jQuery a téměř každý kus kódu JavaScript, který čtete.

Jak ale vypadají uzávěry v reálném kódování? Podívejte se na tento jednoduchý ukázkový kód:

function showName(firstName, lastName) {
      var nameIntro = "Your name is ";
      // this inner function has access to the outer function's variables, including the parameter
      function makeFullName() {
          return nameIntro + firstName + " " + lastName;
      }
      return makeFullName();
  }

  console.log(showName("Michael", "Jackson")); // Your name is Michael Jackson

Také to je klasický způsob uzavírání v jQuery, který každý vývojář javascript a jQuery používal hodně:

$(function() {
    var selections = [];
    $(".niners").click(function() { // this closure has access to the selections variable
        selections.Push(this.prop("name")); // update the selections variable in the outer function's scope
    });
});

Ale proč používáme uzávěry? když ho používáme ve skutečném programování? jaké jsou praktické využití uzávěrů? níže je dobré vysvětlení a příklad MDN:

Praktické uzávěry

Uzávěry jsou užitečné, protože umožňují spojit některá data (lexikální prostředí) s funkcí, která na těchto datech pracuje. To má zjevné paralely s objektově orientovaným programováním, kde nám objekty umožňují spojit některá data (vlastnosti objektu) s jednou nebo více metodami.

V důsledku toho můžete použít uzávěr kdekoli, kde byste mohli objekt normálně používat pouze jednou metodou.

Situace, kdy byste to chtěli udělat, jsou na webu obzvláště běžné. Hodně z kódu, který píšeme v JavaScriptu na frontě, je založeno na událostech - definujeme nějaké chování, pak jej připojíme k události, kterou spustil uživatel (např. Kliknutí nebo stisknutí klávesy). Náš kód je obecně připojen jako zpětné volání: jedna funkce, která je provedena v reakci na událost.

Předpokládejme například, že chceme přidat některá tlačítka na stránku, která upraví velikost textu. Jedním ze způsobů, jak toho dosáhnout, je určit velikost písma elementu těla v obrazových bodech a potom nastavit velikost ostatních prvků na stránce (například záhlaví) pomocí relativní jednotky em:

Přečtěte si níže uvedený kód a spusťte kód, abyste zjistili, jak nám uzavření pomáhá snadno vytvářet oddělené funkce pro jednotlivé sekce:

//javascript
function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
/*css*/
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
<!--html><!-->
<p>Some paragraph text</p>
<h1>some heading 1 text</h1>
<h2>some heading 2 text</h2>

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>

Pro další studium o uzávěrech doporučuji navštívit tuto stránku MDN: https://developer.mozilla.org/cs/docs/Web/JavaScript/Closures

34
Alireza

Pro šestiletého?

Vy a vaše rodina žijete v mýtickém městě Ann Ville. Máte přítele, který bydlí vedle, takže jim zavoláte a požádáte je, aby vyšli a hráli. Vytočíte:

000001 (JamiesHouse)

Po měsíci se vy a vaše rodina odstěhujete z Ann Ville do dalšího města, ale vy a váš přítel stále zůstáváte v kontaktu, takže nyní musíte vytočit směrové číslo města, ve kterém váš přítel žije, než vytočí své číslo. správné číslo:

001 000001 (annVille.jamiesHouse)

O rok později se vaši rodiče přestěhují do celé nové země, ale vy a váš přítel stále zůstáváte v kontaktu, takže po odposlechnutí rodičů, kteří vám umožní uskutečňovat mezinárodní hovory, vytočíte:

01 001 000001 (myOldCountry.annVille.jamiesHouse)

Kupodivu se však po přestěhování do vaší nové země stěhujete do nového města jménem Ann Ville ... a vy se tak stane, abyste se s někým novým pojmenovali Jamie ...\t volání...

000001 (JamiesHouse)

Strašidelný...

Ve skutečnosti je to strašidelné, že o tom řeknete Jamie ze své staré země ... Máte na tom dobrý smích. Jednoho dne si vy a vaše rodina vezmete dovolenou zpět do staré země. Navštívíte své staré město (Ann Ville) a jdete navštívit Jamie ...

  • "Opravdu? Další Jamie? V Ann Ville? Ve vaší nové zemi!"
  • "Jo ... Říkejme jim ..."

02 001 000001 (myNewCountry.annVille.jamiesHouse)

Názory?

A co víc, mám spoustu otázek o trpělivosti moderního šestiletého ...

30
Charlie

V JavaScriptu uzávěry jsou úžasné, kde proměnné nebo argumenty jsou k dispozici vnitřním funkcím, a budou naživu i poté, co se vrátí vnější funkce.

  function getFullName(a, b) {
  return a + b;
}

function makeFullName(fn) {

  return function(firstName) {

    return function(secondName) {

      return fn(firstName, secondName);
    }
  }
}

makeFullName(getFullName)("stack")("overflow"); // Stackoverflow
29
Shushanth Pallegar

Zde je jednoduchý scénář v reálném čase. Prostě si to přečtěte a pochopíte, jak jsme zde uzavřeli (viz, jak se mění číslo sedadla).

Všechny ostatní příklady, které byly dříve vysvětleny, jsou také velmi dobré pro pochopení pojmu.

function movieBooking(movieName) {
    var bookedSeatCount = 0;
    return function(name) {
        ++bookedSeatCount ;
        alert( name + " - " + movieName + ", Seat - " + bookedSeatCount )
    };
};

var MI1 = movieBooking("Mission Impossible 1 ");
var MI2 = movieBooking("Mission Impossible 2 ");

MI1("Mayur");
// alert
// Mayur - Mission Impossible 1, Seat - 1

MI1("Raju");
// alert
// Raju - Mission Impossible 1, Seat - 2

MI2("Priyanka");
// alert
// Raja - Mission Impossible 2, Seat - 1
28
Mayur Randive

Uzávěry umožňují programátorům JavaScriptu psát lepší kód. Tvůrčí, expresivní a výstižné. Často používáme uzávěry v JavaScriptu a bez ohledu na naše zkušenosti s JavaScriptem se s nimi nepochybně setkáváme znovu a znovu. Uzávěry se mohou zdát složité, ale doufejme, že po přečtení budou uzávěry mnohem srozumitelnější a tak přitažlivější pro každodenní programovací úkoly jazyka JavaScript.

Měli byste být obeznámeni s JavaScript variabilní rozsah předtím, než budete číst dále, protože pro pochopení uzávěrů musíte porozumět proměnnému rozsahu JavaScriptu.

Co je to uzavření?

Uzavření je vnitřní funkce, která má přístup k proměnným vnější (obklopující) funkce - řetězci rozsahu. Uzávěr má tři řetězy rozsahu: má přístup ke svému vlastnímu rozsahu (proměnné definované mezi jeho složenými závorkami), má přístup k proměnným vnější funkce a má přístup ke globálním proměnným.

Vnitřní funkce má přístup nejen k proměnným vnější funkce, ale také k parametrům vnější funkce. Všimněte si, že vnitřní funkce nemůže volat objekt argumentu vnější funkce, i když může přímo volat parametry vnější funkce.

Uzavření vytvoříte přidáním funkce do jiné funkce.

Základní příklad uzavření v JavaScriptu:

function showName (firstName, lastName) {

  var nameIntro = "Your name is ";
  // this inner function has access to the outer function's variables, including the parameter
  ​function makeFullName () {
            
​    return nameIntro + firstName + " " + lastName;
        
  }
​
​  return makeFullName ();

}

​
showName ("Michael", "Jackson"); // Your name is Michael Jackson


Uzávěry jsou používány značně v Node.js; jsou to pracovní koně v asynchronní, neblokující architektuře Node.js. Uzávěry jsou také často používány v jQuery a téměř každý kus kódu JavaScript, který čtete.

Klasický příklad jQuery uzávěrů:

$(function() {
​
​  var selections = []; 
  $(".niners").click(function() { // this closure has access to the selections variable​
    selections.Push (this.prop("name")); // update the selections variable in the outer function's scope​
  });
​});

Pravidla uzavření a vedlejší účinky

1. Uzávěry mají přístup k proměnné vnější funkce i poté, co vnější funkce vrátí:

Jedním z nejdůležitějších a nejkrásnějších rysů uzávěrů je, že vnitřní funkce má stále přístup k proměnným vnější funkce, a to i poté, co se vnější funkce vrátila. Jo, čtete to správně. Když se funkce v JavaScriptu provádějí, používají stejný řetězec rozsahu, který byl platný, když byly vytvořeny. To znamená, že i když se vnější funkce vrátí, vnitřní funkce má stále přístup k proměnným vnější funkce. Proto můžete vnitřní funkci zavolat později v programu. Tento příklad ukazuje:

function celebrityName (firstName) {
    var nameIntro = "This celebrity is ";
    // this inner function has access to the outer function's variables, including the parameter​
   function lastName (theLastName) {
        return nameIntro + firstName + " " + theLastName;
    }
    return lastName;
}
​
​var mjName = celebrityName ("Michael"); // At this juncture, the celebrityName outer function has returned.​
​
​// The closure (lastName) is called here after the outer function has returned above​
​// Yet, the closure still has access to the outer function's variables and parameter​
mjName ("Jackson"); // This celebrity is Michael Jackson


2. Uzávěry ukládají odkazy na proměnné vnější funkce:

Neukládají skutečnou hodnotu. Uzávěry jsou zajímavější, když se hodnota proměnné vnější funkce změní před zavřením uzávěru. A tato mocná funkce může být využita tvůrčími způsoby, jako je tento příklad soukromých proměnných, který poprvé demonstroval Douglas Crockford:

function celebrityID () {
    var celebrityID = 999;
    // We are returning an object with some inner functions​
    // All the inner functions have access to the outer function's variables​
    return {
        getID: function ()  {
            // This inner function will return the UPDATED celebrityID variable​
            // It will return the current value of celebrityID, even after the changeTheID function changes it​
          return celebrityID;
        },
        setID: function (theNewID)  {
            // This inner function will change the outer function's variable anytime​
            celebrityID = theNewID;
        }
    }
​
}
​
​var mjID = celebrityID (); // At this juncture, the celebrityID outer function has returned.​
mjID.getID(); // 999​
mjID.setID(567); // Changes the outer function's variable​
mjID.getID(); // 567: It returns the updated celebrityId variable


3. Uzavření Gone Awry

Protože uzávěry mají přístup k aktualizovaným hodnotám proměnných vnější funkce, mohou také vést k chybám, když se proměnná vnější funkce změní se smyčkou for. Tím pádem:

// This example is explained in detail below (just after this code box).​
​function celebrityIDCreator (theCelebrities) {
    var i;
    var uniqueID = 100;
    for (i = 0; i < theCelebrities.length; i++) {
      theCelebrities[i]["id"] = function ()  {
        return uniqueID + i;
      }
    }
    
    return theCelebrities;
}
​
​var actionCelebs = [{name:"Stallone", id:0}, {name:"Cruise", id:0}, {name:"Willis", id:0}];
​
​var createIdForActionCelebs = celebrityIDCreator (actionCelebs);
​
​var stalloneID = createIdForActionCelebs [0];

    console.log(stalloneID.id()); // 103


Více naleznete zde-

  1. http://javascript.info/tutorial/closures

  2. http://www.javascriptkit.com/javatutors/closures.shtml

26
Abrar Jahin

Zde je zenová odpověď, kterou mohu dát:

Co byste očekávali od tohoto kódu? Řekni mi to v komentáři, než ho spustíš. Jsem zvědavý!

function foo() {
  var i = 1;
  return function() {
    console.log(i++);
  }
}

var bar = foo();
bar();
bar();
bar();

var baz = foo();
baz();
baz();
baz();

Nyní otevřete konzoli v prohlížeči (Ctrl + Shift + I nebo F12, doufejme) a vložte kód a stiskněte Enter.

Pokud tento kód vytiskne to, co očekáváte (JavaScript newbies - ignorujte "undefined" na konci), pak již máte bez slov. Slovně, proměnná i je součástí vnitřní funkce instance uzavření.

Takhle jsem to vysvětlil, protože jakmile jsem pochopil, že tento kód vkládá instance foo() vnitřní funkce do bar a baz a pak je volá přes tyto proměnné, nic jiného mě nepřekvapilo.

Ale pokud se mýlím a konzolový výstup vás překvapil, dejte mi vědět!

24
Andy

Vzhledem k následující funkci

function person(name, age){

    var name = name;
    var age = age;

    function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }

    return introduce;
}

var a = person("Jack",12);
var b = person("Matt",14);

Pokaždé, když se funkce person nazývá nový uzávěr. Zatímco proměnné a a b mají stejnou funkci introduce, je spojena s různými uzávěry. A že uzavření bude stále existovat i po dokončení funkce person.

Enter image description here

a(); //My name is Jack, and I'm 12
b(); //My name is Matt, and I'm 14

Abstraktní uzávěry by mohly být reprezentovány na něco takového:

closure a = {
    name: "Jack",
    age: 12,
    call: function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }
}

closure b = {
    name: "Matt",
    age: 14,
    call: function introduce(){
        alert("My name is "+name+", and I'm "+age);
    }
}

Za předpokladu, že víte, jak class v jiné jazykové práci, udělám analogii.

Přemýšlejte

  • JavaScript function jako constructor
  • local variables jako instance properties
  • tyto properties jsou soukromé
  • inner functions jako instance methods

Vždy se volá function

  • Bude vytvořen nový object obsahující všechny lokální proměnné.
  • Metody tohoto objektu mají přístup k "properties" objektu instance.
22
Vitim.us

I když na Internetu existuje mnoho krásných definic JavaScriptových uzávěrů, snažím se vysvětlit svého šestiletého přítele s mými oblíbenými definicemi uzavření, které mi pomohly pochopit uzávěr mnohem lépe.

Co je to uzavření?

Uzavření je vnitřní funkce, která má přístup k proměnným vnější (obklopující) funkce - řetězci rozsahu. Uzávěr má tři řetězy rozsahu: má přístup ke svému vlastnímu rozsahu (proměnné definované mezi jeho složenými závorkami), má přístup k proměnným vnější funkce a má přístup ke globálním proměnným.

Uzavření je lokální proměnná pro funkci - po návratu funkce se udržuje naživu.

Uzávěry jsou funkce, které odkazují na nezávislé (volné) proměnné. Jinými slovy, funkce definovaná v závěru „si pamatuje“ prostředí, ve kterém bylo vytvořeno.

Uzávěry jsou rozšířením pojmu rozsah. S uzávěry mají funkce přístup k proměnným, které byly k dispozici v oblasti, kde byla funkce vytvořena.

Uzávěr je rám zásobníku, který není uvolněn, když se funkce vrací. (Jako by byl 'stack-frame' malloc'ed místo toho, aby byl na stacku!)

Jazyky jako Java poskytují schopnost deklarovat metody soukromé, což znamená, že mohou být volány pouze jinými metodami ve stejné třídě. JavaScript neposkytuje nativní způsob, jak toho dosáhnout, ale je možné emulovat soukromé metody pomocí uzávěrů.

"Uzavření" je výraz (obvykle funkce), který může mít volné proměnné spolu s prostředím, které tyto proměnné váže (což "uzavře" výraz).

Uzávěry jsou mechanismus abstrakce, který vám umožňuje oddělit obavy velmi čistě.

Použití uzávěrů:

Uzávěry jsou užitečné pro skrytí implementace funkčnosti a zároveň odhalení rozhraní.

Koncept zapouzdření můžete emulovat pomocí JavaScriptu.

Uzávěry jsou používány značně v jQuery a Node.js .

Zatímco objektové literály jsou jistě snadno vytvořitelné a vhodné pro ukládání dat, uzávěry jsou často lepší volbou pro vytváření statických singletonových jmenných prostorů ve velké webové aplikaci.

Příklad uzavření:

Za předpokladu, že můj šestiletý přítel pozná sčítání v nedávné době na základní škole, měl jsem pocit, že tento příklad přidání dvou čísel by byl nejjednodušší a vhodný pro šestiletého učitele.

Příklad 1: Uzavření je dosaženo zde vrácením funkce.

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

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

Příklad 2: Uzavření je dosaženo zde vrácením doslovného objektu.

function makeAdder(x) {
    return {
        add: function(y){
            return x + y;
        }
    }
}

var add5 = makeAdder(5);
console.log(add5.add(2));//7

var add10 = makeAdder(10);
console.log(add10.add(2));//12

Příklad 3: Uzavření v jQuery

$(function(){
    var name="Closure is easy";
    $('div').click(function(){
        $('p').text(name);
    });
});

Užitečné odkazy:

Díky výše uvedeným odkazům, které mi pomáhají lépe porozumět a vysvětlit uzavření.

21
Ravi

Abychom porozuměli uzávěrkám, musíme se dostat do programu a doslova spustit, jako kdyby jste byli v době běhu. Podívejme se na tento jednoduchý kód:

 Enter image description here

JavaScript spustí kód ve dvou fázích:

  • Fáze kompilace // JavaScript není čistě interpretovaný jazyk
  • Prováděcí fáze

Když JavaScript projde fází kompilace, vyjme deklarace proměnných a funkcí. Toto se nazývá zvedání. Funkce, s nimiž se v této fázi setkáváme, se ukládají jako textové bloky v paměti, známé také jako lambda. Po kompilaci přejde JavaScript do prováděcí fáze, kde přiřadí všechny hodnoty a spustí funkci. Pro spuštění funkce připraví kontext provádění přiřazením paměti z haldy a opakováním fáze kompilace a provádění této funkce. Tato oblast paměti se nazývá rozsah funkce. Při spuštění je globální rozsah. Rozsah je klíčem k pochopení uzávěrů.

V tomto příkladu je nejprve definována proměnná a a pak je ve fázi kompilace definována f. Všechny nedeklarované proměnné jsou uloženy v globálním rozsahu. Ve fázi provádění f se volá s argumentem. Rozsah f je přiřazen a fáze kompilace a provádění se opakuje.

Argumenty jsou také uloženy v tomto lokálním rozsahu pro f. Kdykoli je vytvořen místní kontext nebo rozsah, obsahuje odkaz na jeho nadřazený rozsah. Všechny proměnné přístup následuje tento lexikální rozsah řetězce najít jeho hodnotu. Pokud proměnná není nalezena v lokálním rozsahu, následuje řetězec a nachází se v nadřazeném rozsahu. To je také důvod, proč místní proměnná přepíše proměnné v nadřazeném oboru. Nadřazený rozsah se nazývá "Uzavření" pro místní rozsah nebo funkci.

Když je nastaven rozsah g, dostal lexikální ukazatel na rozsah rodičů f. Rozsah f je uzavření g. V JavaScriptu, pokud existuje nějaká zmínka o funkcích, objektech nebo dalekohledech, pokud se jich nějakým způsobem podaří dosáhnout, nedostane se odpad. Takže když je spuštěn myG, má ukazatel na rozsah f, což je jeho uzavření. Tato oblast paměti nedostane odpadky shromážděné i f se vrátil. Jedná se o uzavření, pokud jde o dobu běhu.

TAK JAK IS ZAVŘÍT?

  • Je to implicitní, trvalé spojení mezi funkcí a řetězcem jeho rozsahu ...
  • Definice funkce (lambda) skrytá [[scope]] reference.
  • Drží řetěz rozsahu (zabraňuje sběru odpadu).
  • Používá se a kopíruje se jako "vnější odkaz na prostředí" kdykoliv je funkce spuštěna.

IMPLICIT CLOSURE

var data = "My Data!";
setTimeout(function() {
  console.log(data); // Prints "My Data!"
}, 3000);

EXPLICIT CLOSURES

function makeAdder(n) {
  var inc = n;
  var sum = 0;
  return function add() {
    sum = sum + inc;
    return sum;
  };
}

var adder3 = makeAdder(3);

Velmi zajímavý rozhovor o uzavření a více jeArindam Paul - JavaScript VM internals, EventLoop, Async a ScopeChains.

21
Nikhil Ranjan

Uzavření je funkce v rámci funkce , která má přístup k proměnným a parametrům "mateřské" funkce.

Příklad:

function showPostCard(Sender, Receiver) {

    var PostCardMessage = " Happy Spring!!! Love, ";

    function PreparePostCard() {
        return "Dear " + Receiver + PostCardMessage + Sender;
    }

    return PreparePostCard();
}
showPostCard("Granny", "Olivia");
21
enb081

Seznamte se s ilustrovaným vysvětlením : Jak fungují záběry JavaScriptu v zákulisí.

Článek vysvětluje, jak jsou objekty oboru (nebo LexicalEnvironments) přidělovány a používány intuitivním způsobem. Stejně jako u tohoto jednoduchého skriptu:

"use strict";

var foo = 1;
var bar = 2;

function myFunc() {
  //-- Define local-to-function variables
  var a = 1;
  var b = 2;
  var foo = 3;
}

//-- And then, call it:
myFunc();

Při provádění kódu nejvyšší úrovně máme následující uspořádání objektů oboru:

 Enter image description here

Když se nazývá myFunc(), máme následující řetězec rozsahu:

 Enter image description here

Pochopení toho, jak jsou objekty rozsahu vytvářeny, používány a vymazávány, je klíčem k tomu, abyste měli velký obraz a pochopili, jak fungují uzávěry pod kapotou.

Podrobnosti naleznete ve výše uvedeném článku.

21
Dmitry Frank

Obrázek verze pro tuto odpověď: [Vyřešeno]

Zapomeňte na rozsah každé věci a nezapomeňte: Když je někde potřeba nějaká proměnná, javascript ji nezničí. Proměnná vždy ukazuje na nejnovější hodnotu.

Příklad 1:

 enter image description here

Příklad 2:

 enter image description here

Příklad 3:  enter image description here

20
christian Nguyen

(Nezohledňuji 6letou věc.)

V jazyce, jako je JavaScript, kde můžete předávat funkce jako parametry jiným funkcím (jazyky, kde jsou funkce občané první třídy ), často se ocitnete v tom, že děláte něco jako:

var name = 'Rafael';

var sayName = function() {
  console.log(name);
};

Vidíte, sayName nemá definici proměnné name, ale používá hodnotu name, která byla definována mimo sayName (v nadřazeném oboru).

Řekněme, že předáte sayName jako parametr jiné funkci, která zavolá sayName jako zpětné volání:

functionThatTakesACallback(sayName);

Všimněte si, že:

  1. sayName bude voláno z functionThatTakesACallback (předpokládejme, že v tomto příkladu jsem functionThatTakesACallback neimplementoval).
  2. Když je voláno sayName, zapíše hodnotu proměnné name.
  3. functionThatTakesACallback nedefinuje proměnnou name (dobře, mohlo by to, ale nezáleželo na tom, tak předpokládejme, že tomu tak není).

Takže máme sayName, které se nazývá functionThatTakesACallback a odkazuje na proměnnou name, která není definována uvnitř functionThatTakesACallback.

Co se pak stane? A ReferenceError: name is not defined?

Ne! Hodnota name je zachycena uvnitř uzavření. Tento závěr lze považovat za kontext spojený s funkcí, který obsahuje hodnoty, které byly k dispozici, kde byla tato funkce definována.

Takže: I když name není v rozsahu, ve kterém bude funkce sayName nazývána (uvnitř functionThatTakesACallback), sayName může přistupovat k hodnotě name, která je zachycena v uzávěru souvisejícím s sayName.

-

Z knihy výmluvný JavaScript :

Dobrým mentálním modelem je přemýšlet o hodnotách funkcí, které obsahují jak kód v jejich těle, tak prostředí, ve kterém jsou vytvořeny. Když je nazýváno, tělo funkce vidí své původní prostředí, nikoli prostředí, ve kterém je volání provedeno.

20
Rafael Eyng

Tato odpověď je souhrnem tohoto youtube videa Javascript Closures . Takže plné kredity na toto video.

Uzávěry nejsou nic jiného než stavové funkce, které udržují stavy svých soukromých proměnných.

Obvykle při volání funkce, jak je znázorněno na obrázku níže. Proměnné jsou vytvořeny v zásobníku (běžící RAM paměť) použité a pak disallocated.

 enter image description here

Ale nyní existují situace, kdy chceme zachovat tento stav funkce, kde se používá zavírání Javascriptu. Uzavření je funkce uvnitř funkce s návratem, jak je uvedeno v níže uvedeném kódu.

 enter image description here

Takže uzavírací kód pro funkci čítače výše vypadá něco, co je uvedeno níže. Uvádí funkci uvnitř funkce s příkazem return.

function Counter() {
           var counter = 0;

           var Increment = function () {
               counter++;
               alert(counter);
           }
           return {
               Increment
           }
       }

Teď, když zavoláte, čítač se zvýší jinými slovy, volání funkce udržuje stavy.

var x = Counter(); // get the reference of the closure
x.Increment(); // Displays 1
x.Increment(); // Display 2 ( Maintains the private variables)

Ale teď největší otázkou je použití takovéto stavové funkce. Stavové funkce jsou stavební bloky pro implementaci konceptu OOP jako abstrakce, zapouzdření a vytváření samostatných modulů.

Takže cokoliv, co chcete zapouzdřit, můžete dát jako soukromé a věci, které mají být vystaveny veřejnosti, by měly být vloženy do prohlášení o návratu. Také tyto komponenty jsou samy o sobě izolované objekty, takže neznečišťují globální proměnné.

Objekt, který následuje OOP principy, je samostatný, následuje abstrakci, následuje zapouzdření a tak. S uzavřením v Javascriptu je to obtížné implementovat.

 enter image description here

18

Následující příklad je jednoduchý příklad uzavření JavaScriptu. Jedná se o funkci uzavření, která vrací funkci s přístupem k místní proměnné x,

function outer(x){
     return function inner(y){
         return x+y;
     }
}

Tuto funkci vyvolejte takto:

var add10 = outer(10);
add10(20); // The result will be 30
add10(40); // The result will be 50

var add20 = outer(20);
add20(20); // The result will be 40
add20(40); // The result will be 60
18
Mohammed Safeer

Uzavření je něco, co mnoho vývojářů JavaScriptu používá po celou dobu, ale bereme to jako samozřejmost. Jak to funguje, není tak složité. Pochopení toho, jak ho účelně používat je komplex.

U jeho nejjednodušší definice (jak jiné odpovědi poukázaly na), uzavření je v podstatě funkce definovaná uvnitř jiné funkce. A tato vnitřní funkce má přístup k proměnným definovaným v rozsahu vnější funkce. Nejběžnější praxí, kterou uvidíte pomocí uzávěrů, je definování proměnných a funkcí v globálním rozsahu a přístup k těmto proměnným v rozsahu funkce této funkce.

var x = 1;
function myFN() {
  alert(x); //1, as opposed to undefined.
}
// Or
function a() {
   var x = 1;
   function b() {
       alert(x); //1, as opposed to undefined.
   }
   b();
}

No a co?

Uzavření není pro uživatele JavaScriptu tak zvláštní, dokud neuvažujete o tom, jaký by byl život bez nich. V jiných jazycích se proměnné použité ve funkci vyčistí, když se tato funkce vrátí. Ve výše uvedeném by x bylo "nulovým ukazatelem" a bylo by nutné vytvořit si getter a setter a začít projít odkazy. Nezní jako JavaScript správně? Děkuji mocnému uzavření.

Proč bych se měl starat?

Nemusíte si opravdu být vědomi uzavření, abyste je mohli používat. Jak však také upozornili jiní, mohou být pákovým efektem k vytváření falešných soukromých proměnných. Dokud se nedostanete k potřebě soukromých proměnných, stačí je použít jako vždy.

17
Harry Robbins

Z osobního příspěvku blogu :

Ve výchozím nastavení JavaScript zná dva typy oborů: globální a místní.

var a = 1;

function b(x) {
    var c = 2;
    return x * c;
}

Ve výše uvedeném kódu jsou proměnná a a funkce b k dispozici odkudkoli v kódu (tj. Globálně). Proměnná c je k dispozici pouze v rozsahu b funkce (tj. Lokální). Většina vývojářů softwaru nebude spokojena s tímto nedostatkem flexibility, zejména ve velkých programech.

Uzávěry JavaScriptu pomáhají řešit tento problém vázáním funkce s kontextem:

function a(x) {
    return function b(y) {
        return x + y;
    }
}

Funkce a vrací funkci nazvanou b. Protože b je definováno v rámci a, má automaticky přístup k tomu, co je definováno v a, tj. x v tomto příkladu. Proto b může vrátit x + y bez deklarace x.

var c = a(3);

Proměnná c je přiřazena výsledek volání a parametru 3. To je instance funkce b kde x = 3. Jinými slovy, c je nyní funkce ekvivalentní:

var c = function b(y) {
    return 3 + y;
}

Funkce b si pamatuje, že x = 3 ve svém kontextu. Proto:

var d = c(4);

přiřadí hodnotu 3 + 4 d, tj. 7.

Poznámka : Pokud někdo upraví hodnotu x (řekni x = 22) poté, co byla vytvořena instance funkce b, bude to také zohledněno v b. Proto by pozdější volání c (4) vrátilo 22 + 4, tj. 26.

Uzávěry lze také použít k omezení rozsahu proměnných a metod deklarovaných globálně:

(function () {
    var f = "Some message";
    alert(f);
})();

Výše uvedené je uzavření, kde funkce nemá žádné jméno, žádný argument a je volána okamžitě. Zvýrazněný kód, který deklaruje globální proměnnou f, omezuje rozsah f na uzavření.

Nyní existuje běžná nápověda JavaScriptu, kde mohou pomoci uzávěry:

var a = new Array();

for (var i=0; i<2; i++) {
    a[i]= function(x) { return x + i ; }
}

Z výše uvedeného by většina předpokládala, že pole a bude inicializováno následovně:

a[0] = function (x) { return x + 0 ; }
a[1] = function (x) { return x + 1 ; }
a[2] = function (x) { return x + 2 ; }

Ve skutečnosti je to způsob inicializace, protože poslední hodnota i v kontextu je 2:

a[0] = function (x) { return x + 2 ; }
a[1] = function (x) { return x + 2 ; }
a[2] = function (x) { return x + 2 ; }

Řešením je:

var a = new Array();

for (var i=0; i<2; i++) {
    a[i]= function(tmp) {
        return function (x) { return x + tmp ; }
    } (i);
}

Argument/variable tmp uchovává při vytváření instancí funkcí lokální kopii měnící se hodnoty i.

17

Zjistil jsem velmi jasnou kapitolu 8, část 6, "Uzavření", z JavaScript: Definitivní průvodce Davida Flanagana, 6. vydání, O'Reilly, 2011. Pokusím se parafrázovat.

  1. Když je funkce vyvolána, vytvoří se nový objekt, který bude obsahovat lokální proměnné pro toto vyvolání.

  2. Rozsah funkce závisí na umístění deklarace, nikoli na místě jejího provedení.

Předpokládejme vnitřní funkci deklarovanou v rámci vnější funkce a odkazující na proměnné této vnější funkce. Dále předpokládejme, že vnější funkce vrací vnitřní funkci jako funkci. Nyní existuje externí odkaz na hodnoty, které byly v rozsahu vnitřní funkce (které podle našich předpokladů zahrnují hodnoty z vnější funkce).

JavaScript tyto hodnoty zachová, protože zůstaly v rozsahu současného provedení díky tomu, že byly předány z dokončené vnější funkce. Všechny funkce jsou uzávěry, ale uzávěry zájmu jsou vnitřní funkce, které v našem předpokládaném scénáři zachovávají hodnoty vnějších funkcí v rámci jejich "Enclosure" (doufám, že zde jazyk správně používám), když se vrátí (vnitřní funkce). z vnějších funkcí. Vím, že to nesplňuje šestiletý požadavek, ale doufejme, že je to stále užitečné.

17
Jim

Jsem si jistý, Einstein neřekl to s přímým očekáváním, že si vybereme jakoukoli věc esoterického brainstormera a že budeme běhat přes šestileté děti s marnými pokusy získat ty „šílené“ (a co je ještě horší) pro ně-nudné) věci pro jejich dětinské mysli :) Kdybych měl šest let, nechtěl bych mít takové rodiče nebo by se přátelství s tak nudnými filantropy, omlouvám :)

Každopádně, pro děti, uzavření je prostě objetí , myslím, bez ohledu na to, jakým způsobem se snažíte vysvětlit :) A když obejdete svého přítele, pak si oba druh něčeho sdílíte kluci mají v tuto chvíli. Je to obřad průchodu, jakmile jste někoho objali, ukazujete její důvěru a ochotu nechat ji, aby s vámi udělala spoustu věcí, které nedovolíte a které by se skryly před ostatními. Je to akt přátelství :).

Opravdu nevím, jak to vysvětlit dětem ve věku 5-6 let. Nemyslím si, že ocení jakýkoli úryvek kódu JavaScriptu, jako jsou:

function Baby(){
    this.iTrustYou = true;
}

Baby.prototype.hug = function (baby) {
    var smiles = 0;

    if (baby.iTrustYou) {
        return function() {
            smiles++;
            alert(smiles);
        };
    }
};

var
   arman = new Baby("Arman"),
   morgan = new Baby("Morgana");

var hug = arman.hug(morgan);
hug();
hug();

Pouze pro děti:

Uzavření is objetí

Chyba is letět

POLIBEKje hladký! :)

15

Pokud to chcete vysvětlit šestiletému dítěti, pak musíte najít něco mnohem jednoduššího a NO kód.

Stačí říct dítěti, že je "otevřený", který říká, že je schopen mít vztahy s některými dalšími, jeho přáteli. V určitém okamžiku určil přátele (můžeme znát jména jeho přátel), to je uzavření. Pokud vyfotíte jeho a jeho přátele, pak je "zavřený" relativně k jeho přátelským schopnostem. Ale obecně je "otevřený". Během celého svého života bude mít mnoho různých sad přátel. Jedním z těchto setů je uzávěr.

14

Funkce se provádí v rozsahu objektu/funkce, ve které je definována. Tato funkce může přistupovat k proměnným definovaným v objektu/funkci, kde byla definována, zatímco je prováděna.

A vezměte si to doslova .... jak je kód napsán: P

14
moha297

Pokud tomu dobře rozumíte, můžete to vysvětlit jednoduše. A nejjednodušší způsob je abstrahovat od kontextu. Odložte stranou, dokonce i programujte stranou. Příklad metafory to udělá lépe.

Představme si, že funkce je místnost, jejíž stěny jsou ze skla, ale jsou to speciální sklo, jako jsou místnosti ve vyšetřovací místnosti. Z vnějšku jsou neprůhledné, zevnitř jsou průhledné. Mohou to být pokoje v jiných místnostech a jediným způsobem kontaktu je telefon.

Pokud zavoláte zvenčí, nevíte, co je v něm, ale víte, že lidé, kteří jsou uvnitř, vykonají úkol, pokud jim dáte určité informace. Mohou vidět venku, takže se vás mohou zeptat na věci, které jsou venku, a dělat změny v této věci, ale nemůžete změnit to, co je uvnitř zvenčí, ani nevidíte (nevíte), co je uvnitř. Lidé uvnitř té místnosti, kterou voláte, vidí, co je venku, ale ne to, co je uvnitř místností v místnosti, takže s nimi komunikují tak, jak to děláte zvenčí. Lidé uvnitř nejvnitřnějších místností vidí mnoho věcí, ale lidé z nejvzdálenějších místností ani nevědí o existenci většiny vnitřních místností.

Pro každé volání do vnitřní místnosti si lidé v této místnosti uchovávají záznamy o informacích o tomto konkrétním volání a jsou tak dobří, že si nikdy nepletou jednu výzvu s jinými věcmi.

Pokoje jsou funkce, viditelnost je rozsah, lidé dělají úkol je prohlášení, věci jsou objekty, telefonní hovory jsou funkce volání, telefonní hovory informace jsou argumenty, volání záznamy jsou instancí rozsahu, nejvíce vnější místnost je globální objekt.

13
Juan Garcia

Uzávěry jsou prostředky, kterými mohou vnitřní funkce odkazovat na proměnné přítomné v jejich vnější uzavírací funkci po ukončení rodičovských funkcí.

// A function that generates a new function for adding numbers.
function addGenerator( num ) {
    // Return a simple function for adding two numbers
    // with the first number borrowed from the generator
    return function( toAdd ) {
        return num + toAdd
    };
}

// addFive now contains a function that takes one argument,
// adds five to it, and returns the resulting number.
var addFive = addGenerator( 5 );
// We can see here that the result of the addFive function is 9,
// when passed an argument of 4.
alert( addFive( 4 ) == 9 );
13
ketan

Uzavření je funkce, která má přístup k nadřazenému rozsahu i po zavření rodičovské funkce.

var add = (function() {
  var counter = 0;
  return function() {
    return counter += 1;
  }
})();

add();
add();
add();
// The counter is now 3

Příklad je vysvětlen:

  • Proměnná add je přiřazena návratová hodnota funkce samovolného vyvolání.
  • Funkce samo-vyvolání se spustí pouze jednou. Nastaví čítač na nulu (0) a vrátí výraz funkce.
  • Tímto způsobem se přidává funkce. "Báječná" část je, že může přistupovat k čítači v nadřazeném rozsahu.
  • Toto se nazývá uzavření JavaScriptu. To umožňuje, aby funkce měla "soukromé" proměnné.
  • Čítač je chráněn rozsahem anonymní funkce a lze jej změnit pouze pomocí funkce přidání.

Zdroj

13
Premraj

Představte si, že ve vašem městě je velmi rozsáhlý park, kde vidíte kouzelníka jménem Mr. Coder, který začíná hrát baseballové zápasy v různých koutech parku pomocí své kouzelnické hůlky zvané JavaScript.

Přirozeně každý baseballový zápas má přesně stejná pravidla a každá hra má své vlastní skóre.

Přirozeně, skóre jednoho baseballového zápasu jsou úplně oddělené od ostatních her.

Uzavření je zvláštní způsob, jak Mr.Coder udržuje skóre všech svých magických baseballových her odděleně.

13
b_dev

Pinocchio: Uzavření v roce 1883 (přes století před JavaScriptem)

Myslím, že to může být nejlépe vysvětleno 6-letému s pěkným dobrodružstvím ... Část { Dobrodružství Pinocchio kde Pinocchio je spolknuta nadměrným pejskem ...

var tellStoryOfPinocchio = function(original) {

  // Prepare for exciting things to happen
  var pinocchioFindsMisterGeppetto;
  var happyEnding;

  // The story starts where Pinocchio searches for his 'father'
  var pinocchio = {
    name: 'Pinocchio',
    location: 'in the sea',
    noseLength: 2
  };

  // Is it a dog... is it a fish...
  // The dogfish appears, however there is no such concept as the belly
  // of the monster, there is just a monster...
  var terribleDogfish = {
    swallowWhole: function(snack) {
      // The swallowing of Pinocchio introduces a new environment (for the
      // things happening inside it)...
      // The BELLY closure... with all of its guts and attributes
      var mysteriousLightLocation = 'at Gepetto\'s ship';

      // Yes: in my version of the story the monsters mouth is directly
      // connected to its belly... This might explain the low ratings
      // I had for biology...
      var mouthLocation = 'in the monsters mouth and then outside';

      var puppet = snack;


      puppet.location = 'inside the belly';
      alert(snack.name + ' is swallowed by the terrible dogfish...');

      // Being inside the belly, Pinocchio can now experience new adventures inside it
      pinocchioFindsMisterGeppetto = function() {
        // The event of Pinocchio finding Mister Geppetto happens inside the
        // belly and so it makes sence that it refers to the things inside
        // the belly (closure) like the mysterious light and of course the
        // hero Pinocchio himself!
        alert(puppet.name + ' sees a mysterious light (also in the belly of the dogfish) in the distance and swims to it to find Mister Geppetto! He survived on ship supplies for two years after being swallowed himself. ');
        puppet.location = mysteriousLightLocation;

        alert(puppet.name + ' tells Mister Geppetto he missed him every single day! ');
        puppet.noseLength++;
      }

      happyEnding = function() {
        // The escape of Pinocchio and Mister Geppetto happens inside the belly:
        // it refers to Pinocchio and the mouth of the beast.
        alert('After finding Mister Gepetto, ' + puppet.name + ' and Mister Gepetto travel to the mouth of the monster.');
        alert('The monster sleeps with its mouth open above the surface of the water. They escape through its mouth. ');
        puppet.location = mouthLocation;
        if (original) {
          alert(puppet.name + ' is eventually hanged for his innumerable faults. ');
        } else {
          alert(puppet.name + ' is eventually turned into a real boy and they all lived happily ever after...');
        }
      }
    }
  }

  alert('Once upon a time...');
  alert('Fast forward to the moment that Pinocchio is searching for his \'father\'...');
  alert('Pinocchio is ' + pinocchio.location + '.');
  terribleDogfish.swallowWhole(pinocchio);
  alert('Pinocchio is ' + pinocchio.location + '.');
  pinocchioFindsMisterGeppetto();
  alert('Pinocchio is ' + pinocchio.location + '.');
  happyEnding();
  alert('Pinocchio is ' + pinocchio.location + '.');

  if (pinocchio.noseLength > 2)
    console.log('Hmmm... apparently a little white lie was told. ');
}

tellStoryOfPinocchio(false);

 
13
Ron Deijkers

Možná byste měli zvážit objektově orientovanou strukturu místo vnitřních funkcí. Například:

var calculate = {
    number: 0,
    init: function (num) {
        this.number = num;
    },
    add: function (val) {
        this.number += val;
    },
    rem: function (val) {
        this.number -= val;
    }
};

A přečtěte si výsledek z proměnné calcu.number, která stejně potřebuje "návrat".

12
Psy Chip

Uzavření je blok kódu, který splňuje tři kritéria:

  • Může být předán jako hodnota a

  • vykonává na požádání kdokoli, kdo má tuto hodnotu, kdy

  • to může odkazovat na proměnné od kontextu ve kterém to bylo vytvořeno (to je, to je zavřeno s ohledem na proměnlivý přístup, v matematickém smyslu slova “zavřel”).

(Slovo "uzavření" má ve skutečnosti nepřesný význam a někteří lidé si nemyslí, že kritérium # 1 je součástí definice. Myslím, že je.)

Uzávěry jsou základem funkčních jazyků, ale vyskytují se i v mnoha dalších jazycích (například v anonymních vnitřních třídách jazyka Java). Můžete s nimi dělat super věci: umožňují odložené provedení a některé elegantní triky stylu.

Autor: Paul Cantrell, @ http://innig.net/software/Ruby/closures-in-Ruby

12
Magne

Po vyvolání funkce přejde mimo rozsah. Pokud tato funkce obsahuje něco jako funkci zpětného volání, pak je funkce zpětného volání stále v rozsahu. Pokud funkce zpětného volání odkazuje na některou lokální proměnnou v bezprostředním prostředí rodičovské funkce, pak přirozeně očekáváte, že tato proměnná bude nepřístupná pro funkci zpětného volání a vrátí se nedefinovaná.

Uzávěry zajišťují, že jakákoli vlastnost, na kterou odkazuje funkce zpětného volání, je k dispozici pro použití touto funkcí, a to i v případě, že její rodičovská funkce mohla být mimo rozsah.

12
goonerify

Uzavření je vytvořeno, když je vnitřní funkce nějakým způsobem zpřístupněna jakémukoliv rozsahu mimo vnější funkci.

Příklad:

var outer = function(params){ //Outer function defines a variable called params
    var inner = function(){ // Inner function has access to the params variable of the outer function
        return params;
    }
    return inner; //Return inner function exposing it to outer scope
},
myFunc = outer("myParams");
myFunc(); //Returns "myParams"
11

Uzávěry jsou jednoduché

Pravděpodobně byste neměli říkat šestiletému o uzávěrech, ale pokud tak učiníte, můžete říci, že uzavření dává možnost získat přístup k proměnné deklarované v jiném rozsahu funkcí.

 enter image description here

function getA() {
  var a = [];

  // this action happens later,
  // after the function returned
  // the `a` value
  setTimeout(function() {
    a.splice(0, 0, 1, 2, 3, 4, 5);
  });

  return a;
}

var a = getA();
out('What is `a` length?');
out('`a` length is ' + a.length);

setTimeout(function() {
  out('No wait...');
  out('`a` length is ' + a.length);
  out('OK :|')
});
<pre id="output"></pre>

<script>
  function out(k) {
    document.getElementById('output').innerHTML += '> ' + k + '\n';
  }
</script>
11
Eugene Tiurin

Vzhledem k tomu, že otázka je o tom, jak to vysvětlit jednoduše tak, jako by to bylo na 6-leté, moje odpověď by byla:

"Když deklarujete funkci v JavaScriptu, má navždy přístup ke všem proměnným a funkcím, které byly k dispozici v řádku před touto deklarací funkce. Funkce a všechny vnější proměnné a funkce, ke kterým má přístup, jsou to, čemu říkáme uzavření."

10
Raul Martins

Uzavření jsou poněkud pokročilé a často nepochopené rysy jazyka JavaScript. Jednoduše řečeno, uzávěry jsou objekty, které obsahují funkci a odkaz na prostředí, ve kterém byla funkce vytvořena. Aby však bylo možné plně pochopit uzávěry, existují dva další znaky jazyka JavaScript, které musí být nejprve chápány jako funkce první třídy a vnitřní funkce.

Funkce první třídy

V programovacích jazycích jsou funkce považovány za prvotřídní občany, pokud s nimi lze manipulovat jako s jakýmkoli jiným typem dat. Například funkce první třídy mohou být konstruovány za běhu a přiřazeny proměnným. Mohou být také předány a vráceny jinými funkcemi. Kromě splnění výše uvedených kritérií mají funkce JavaScriptu také své vlastní vlastnosti a metody. Následující příklad ukazuje některé funkce prvotřídních funkcí. V příkladu jsou vytvořeny dvě funkce přiřazené proměnným „foo“ a „bar“. Funkce uložená v „foo“ zobrazí dialog, zatímco „bar“ jednoduše vrátí jakýkoliv argument, který mu byl předán. Poslední řádek příkladu dělá několik věcí. Za prvé, funkce uložená v „baru“ se nazývá „foo“ jako argument. „Bar“ pak vrací funkci „foo“. Nakonec se nazývá vrácený odkaz „foo“, což způsobí zobrazení „Hello World!“.

var foo = function() {
  alert("Hello World!");
};

var bar = function(arg) {
  return arg;
};

bar(foo)();

Vnitřní funkce

Vnitřní funkce, také označované jako vnořené funkce, jsou funkce, které jsou definovány uvnitř jiné funkce (označované jako vnější funkce). Pokaždé, když je volána vnější funkce, je vytvořena instance vnitřní funkce. Následující příklad ukazuje, jak se používají vnitřní funkce. V tomto případě je funkce add () vnější funkcí. Uvnitř add () je definována a vyvolána vnitřní funkce doAdd ().

function add(value1, value2) {
  function doAdd(operand1, operand2) {
    return operand1 + operand2;
  }

  return doAdd(value1, value2);
}

var foo = add(1, 2);
// foo equals 3

Jednou z důležitých vlastností vnitřních funkcí je, že mají implicitní přístup k rozsahu vnějších funkcí. To znamená, že vnitřní funkce může používat proměnné, argumenty atd. Vnější funkce. V předchozím příkladu byly argumenty "value1" a "value2" add () předány doAdd () jako "operand1 ”A“ operand2 ”argumenty. To je však zbytečné, protože doAdd () má přímý přístup k „value1” a „value2”. Předchozí příklad byl přepsán níže, aby ukázal, jak doAdd () může použít „value1“ a „value2“.

function add(value1, value2) {
  function doAdd() {
    return value1 + value2;
  }

  return doAdd();
}

var foo = add(1, 2);
// foo equals 3

Vytváření uzávěrů

Uzavření je vytvořeno, když je vnitřní funkce zpřístupněna zvenčí funkce, která ji vytvořila. K tomu obvykle dochází, když vnější funkce vrací vnitřní funkci. Když k tomu dojde, vnitřní funkce udržuje odkaz na prostředí, ve kterém byla vytvořena. To znamená, že si pamatuje všechny proměnné (a jejich hodnoty), které byly v té době v rozsahu. Následující příklad ukazuje, jak je uzávěr vytvořen a použit.

function add(value1) {
  return function doAdd(value2) {
    return value1 + value2;
  };
}

var increment = add(1);
var foo = increment(2);
// foo equals 3

O tomto příkladu je třeba poznamenat řadu věcí.

Funkce add () vrací svou vnitřní funkci doAdd (). Vrácením odkazu na vnitřní funkci je vytvořen uzávěr. „Value1“ je lokální proměnná add () a lokální proměnná doAdd (). Non-lokální proměnné odkazují na proměnné, které nejsou ani v lokálním, ani globálním rozsahu. „Value2“ je lokální proměnná doAdd (). Když je volána funkce add (1), vytvoří se uzávěr a uloží se do „přírůstku“. V referenčním prostředí uzávěrky je hodnota „value1“ vázána na hodnotu jedna. Proměnné, které jsou vázány, se také označují za uzavřené. Odtud pochází uzavření jména. Po vyvolání přírůstku (2) se zavře uzavření. To znamená, že se volá doAdd () s proměnnou „value1“, která drží hodnotu jedna. Uzávěr může být v podstatě považován za vytvoření následující funkce.

function increment(value2) {
  return 1 + value2;
}

Kdy použít uzávěry

Uzavření lze použít k dosažení mnoha věcí. Jsou velmi užitečné pro věci, jako je konfigurace funkcí zpětného volání s parametry. Tato část se zabývá dvěma scénáři, kdy uzavření může váš život jako vývojáře výrazně zjednodušit.

Práce s časovači

Uzávěry jsou užitečné při použití ve spojení s funkcemi setTimeout () a setInterval () _. Konkrétněji, uzávěry umožňují předávat argumenty zpětným funkcím funkce setTimeout () a setInterval (). Následující kód například vytiskne řetězec „nějaká zpráva“ jednou za sekundu voláním showMessage ().

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      window.setInterval(showMessage, 1000, "some message<br />");
    });

    function showMessage(message) {
      document.getElementById("message").innerHTML += message;
    }
  </script>
</head>
<body>
  <span id="message"></span>
</body>
</html>

Internet Explorer bohužel nepodporuje předávání argumentů zpětného volání přes setInterval (). Místo zobrazení „nějaké zprávy“ zobrazí aplikace Internet Explorer „nedefinované“ (protože showMessage () není ve skutečnosti předána žádná hodnota). Chcete-li tento problém vyřešit, lze vytvořit uzávěr, který váže argument „zprávy“ na požadovanou hodnotu. Uzavření pak lze použít jako funkci zpětného volání pro setInterval (). Pro ilustraci tohoto konceptu byl kód JavaScript z předchozího příkladu přepsán níže, aby se použilo uzavření.

window.addEventListener("load", function() {
  var showMessage = getClosure("some message<br />");

  window.setInterval(showMessage, 1000);
});

function getClosure(message) {
  function showMessage() {
    document.getElementById("message").innerHTML += message;
  }

  return showMessage;
}

Emulace soukromých dat

Mnoho objektově orientovaných jazyků podporuje koncept dat soukromých členů. JavaScript však není čistě objektově orientovaný jazyk a nepodporuje soukromá data. Je však možné emulovat soukromá data pomocí uzávěrů. Připomeňme, že uzavření obsahuje odkaz na prostředí, ve kterém bylo původně vytvořeno ―, které je nyní mimo rozsah. Protože proměnné v referenčním prostředí jsou přístupné pouze z funkce uzavření, jedná se v podstatě o soukromá data.

Následující příklad ukazuje konstruktor pro jednoduchou třídu osob. Když je vytvořena každá Osoba, dostane jméno prostřednictvím argumentu „jméno“. Osoba interně ukládá své jméno do proměnné „_name“. V návaznosti na dobré objektově orientované programovací postupy je pro načtení jména použita metoda getName ().

function Person(name) {
  this._name = name;

  this.getName = function() {
    return this._name;
  };
}

Stále existuje jeden velký problém s třídou osob. Protože JavaScript nepodporuje soukromá data, nic nebrání tomu, aby někdo jiný přišel a změnil jméno. Následující kód například vytvoří Osobu s názvem Colin a poté změní její název na Tom.

var person = new Person("Colin");

person._name = "Tom";
// person.getName() now returns "Tom"

Osobně bych si to nepřeje, kdyby mohl přijít někdo a legálně změnit mé jméno. Aby se tomu zabránilo, může být použito uzavření, aby proměnná „_name“ byla soukromá. Konstruktor Osoba byl přepsán níže pomocí uzávěru. Všimněte si, že „_name“ je nyní lokální proměnnou konstruktoru namísto vlastnosti objektu. Uzavření je vytvořeno, protože vnější funkce Person ()vystavuje vnitřní funkci vytvořením metody public getName ().

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Když se nazývá getName (), je zaručeno, že vrátí hodnotu, která byla původně předána konstruktoru. Je stále možné, aby někdo přidal k objektu novou vlastnost „_name“, ale interní fungování objektu nebude ovlivněno, pokud odkazují na proměnnou vázanou uzávěrem. Následující kód ukazuje, že proměnná „_name“ je skutečně soukromá.

var person = new Person("Colin");

person._name = "Tom";
// person._name is "Tom" but person.getName() returns "Colin"

Nepoužívat uzávěry

Je důležité pochopit, jak uzávěry fungují a kdy je používat. Stejně důležité je pochopit, kdy nejsou správným nástrojem pro danou práci. Přetížení uzávěrů může způsobit, že skripty budou prováděny pomalu a spotřebovávají zbytečnou paměť. A protože jsou uzávěry tak jednoduché, je možné je zneužít, aniž by o tom věděli. Tato část obsahuje několik scénářů, kde by se uzávěry měly používat opatrně.

V smyčkách

Vytvoření uzávěrů v rámci smyček může mít zavádějící výsledky. Příklad je uveden níže. V tomto příkladu jsou vytvořena tři tlačítka. Když kliknete na tlačítko „button1“, zobrazí se výstraha s nápisem „Kliknuté tlačítko 1“. Podobné zprávy by měly být zobrazeny pro „button2“ a „button3“. Když je však tento kód spuštěn, všechna tlačítka ukazují „Kliknuté tlačítko 4“. Je to proto, že v době, kdy jedno z tlačítek klikne, smyčka dokončila provádění a proměnná smyčky dosáhla své konečné hodnoty čtyři.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Closures</title>
  <meta charset="UTF-8" />
  <script>
    window.addEventListener("load", function() {
      for (var i = 1; i < 4; i++) {
        var button = document.getElementById("button" + i);

        button.addEventListener("click", function() {
          alert("Clicked button " + i);
        });
      }
    });
  </script>
</head>
<body>
  <input type="button" id="button1" value="One" />
  <input type="button" id="button2" value="Two" />
  <input type="button" id="button3" value="Three" />
</body>
</html>

K vyřešení tohoto problému musí být uzávěr oddělen od skutečné proměnné smyčky. To lze provést voláním nové funkce, která zase vytvoří nové referenční prostředí. Následující příklad ukazuje, jak to provést. Proměnná smyčka je předána funkci getHandler (). getHandler () pak vrátí uzávěr, který je nezávislý na původní smyčce „for“.

function getHandler(i) {
  return function handler() {
    alert("Clicked button " + i);
  };
}
window.addEventListener("load", function() {
  for (var i = 1; i < 4; i++) {
    var button = document.getElementById("button" + i);
    button.addEventListener("click", getHandler(i));
  }
});

Nepotřebné použití v konstruktorech

Dalším častým zdrojem zneužití uzávěru jsou funkce konstruktorů. Viděli jsme, jak lze použít uzávěry k emulaci soukromých dat. Je však zbytečné zavádět metody, jako jsou uzávěry, pokud nemají přístup k soukromým datům. Následující příklad se vrátí do třídy Person, ale tentokrát přidá metodu sayHello (), která nepoužívá soukromá data.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };

  this.sayHello = function() {
    alert("Hello!");
  };
}

Pokaždé, když je Osoba instalována, je čas stráven vytvořením metody sayHello (). Pokud je vytvořeno mnoho objektů, stane se to ztráta času. Lepší přístup by bylo přidat sayHello () do prototypu osoby. Přidáním do prototypu mohou všechny objekty osoby sdílet stejnou metodu. To šetří čas v konstruktoru tím, že nemusíte vytvářet uzávěr pro každou instanci. Předchozí příklad je přepsán níže s vnějším uzávěrem přesunutým do prototypu.

function Person(name) {
  var _name = name;

  this.getName = function() {
    return _name;
  };
}

Person.prototype.sayHello = function() {
  alert("Hello!");
};

Co je třeba zapamatovat

  • Uzávěry obsahují funkci a odkaz na prostředí, ve kterém byla funkce vytvořena.
  • Uzavření se vytvoří, když vnější funkce odhalí vnitřní funkci. Uzávěry lze použít pro snadné předávání parametrů zpětným funkcím.
  • Soukromá data lze emulovat pomocí uzávěrů. Toto je běžné v objektově orientovaném programování a designu jmenného prostoru.
  • Uzávěry by neměly být v konstruktorech příliš využívány. Přidání do prototypu je lepší nápad.

Odkaz

10
Durgesh Pandey

Funkce neobsahující žádné volné proměnné se nazývají čisté funkce.

Funkce obsahující jednu nebo více volných proměnných se nazývají uzávěry.

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

var foo = "bar"

var closure = function closure(){
  return foo
  // foo is 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
soundyogi

MDN to nejlépe vysvětluje:

Uzávěry jsou funkce, které odkazují na nezávislé (volné) proměnné. Jinými slovy, funkce definovaná v uzávěru „si pamatuje“ prostředí, ve kterém bylo vytvořeno.

Uzávěr má vždy vnější funkci a vnitřní funkci. Vnitřní funkce je tam, kde se děje veškerá práce a vnější funkcí je právě prostředí, které zachovává rozsah, ve kterém byla vytvořena vnitřní funkce. Tímto způsobem si vnitřní funkce uzávěru „pamatuje“ na prostředí/rozsah, ve kterém byla vytvořena. Nejklasičtějším příkladem je funkce čítače:

var closure = function() {
  var count = 0;
  return function() {
    count++;
    console.log(count);
  };
};

var counter = closure();

counter() // returns 1
counter() // returns 2
counter() // returns 3

Ve výše uvedeném kódu je count zachována vnější funkcí (funkce prostředí), takže pokaždé, když zavoláte counter(), vnitřní funkce (pracovní funkce) ji může zvýšit.

9
Brandon Kent

Mám rád definici uzavření Kyle Simpsona:

Uzavření je, když je funkce schopna si zapamatovat a přistupovat k lexikálnímu rozsahu, i když je tato funkce prováděna mimo její lexikální rozsah.

Lexikální rozsah je, když vnitřní prostor může přistupovat k jeho vnějšímu rozsahu.

Zde je upravený příklad, který uvádí ve své knize "You Don't Know JS: Scopes & Closures".

function foo() {
  var a = 2;

  function bar() {
    console.log( a );
  }
  return bar;
}

function test() {
  var bz = foo();
  bz();
}

// prints 2. Here function bar referred by var bz is outside 
// its lexical scope but it can still access it
test(); 
9
TastyCode

To je, jak začátečník zabalil hlavu kolem Uzávěry jako funkce je zabalen uvnitř těla funkcí také známý jako uzávěry .

Definice z knihy Rozhovor JavaScript "Uzavření je funkce plus spojení s rozsahem, ve kterém byla funkce vytvořena" - Dr.Axel Rauschmayer

Co by to tedy mohlo vypadat? Zde je příklad

function newCounter() {
  var counter = 0;
   return function increment() {
    counter += 1;
   }
}

var counter1 = newCounter();
var counter2 = newCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2
counter2(); // Number of events: 1
counter1(); // Number of events: 3

newCounter zavře přes přírůstek , čítač lze odkazovat na přírůstek .

counter1 a counter2 bude sledovat vlastní hodnotu.

Jednoduché, ale doufejme, jasná perspektiva toho, co uzavření je kolem všech těchto velkých a pokročilých odpovědí.

8
devlighted

Uzavření je, když je funkce zavřená způsobem, který byl definován ve jmenném prostoru, který je neměnný v době, kdy je funkce volána.

V JavaScriptu se to stane, když:

  • Definujte jednu funkci uvnitř druhé funkce
  • Vnitřní funkce je volána po vrácení vnější funkce
// 'name' is resolved in the namespace created for one invocation of bindMessage
// the processor cannot enter this namespace by the time displayMessage is called
function bindMessage(name, div) {

    function displayMessage() {
        alert('This is ' + name);
    }

    $(div).click(displayMessage);
}
8
Pawel Furmaniak

Pro šestiletého ...

Víte, jaké objekty jsou?

Objekty jsou věci, které mají vlastnosti a dělají věci.

Jedna z nejdůležitějších věcí o uzavření je, že vám umožní vytvářet objekty v JavaScriptu. Objekty v JavaScriptu jsou jen funkce a uzávěry, které umožňují JavaScriptu uložit hodnotu vlastnosti objektu, jakmile je vytvořen.

Objekty jsou velmi užitečné a vše pěkné a organizované. Různé objekty mohou dělat různé úlohy a spolupráce objektů může dělat komplikované věci.

Je to štěstí, že JavaScript má uzávěry pro vytváření objektů, jinak by se všechno stalo chaotickou noční můrou.

8
ejectamenta

Nejlepším způsobem je postupné vysvětlování těchto pojmů:

Proměnné

console.log(x);
// undefined

Zde je undefined způsob JavaScriptu, který říká: „Nemám ponětí, co x znamená.“

Proměnné jsou podobné značkám.

Můžete říci, tag x body k hodnotě 42:

var x = 42;
console.log(x);
// 42

JavaScript nyní ví, co znamená x.

Proměnnou můžete také znovu přiřadit.

Značka x ukazuje na jinou hodnotu:

x = 43;
console.log(x);
// 43

x znamená něco jiného.

Rozsah

Když provedete funkci, funkce má vlastní "box" pro proměnné.

function A() {
  var x = 42;
}

console.log(x);

// undefined

Z vnější strany krabice nevidíte, co je uvnitř krabice.

Ale zevnitř krabice můžete vidět, co je mimo krabici:

var x = 42;

function A() {
  console.log(x);
}

// 42

Uvnitř funkce A, máte "rozsah přístupu" k x.

Pokud máte dvě krabice vedle sebe:

function A() {
  var x = 42;
}

function B() {
  console.log(x);
}

// undefined

Uvnitř funkce B nemáte přístup k proměnným uvnitř funkce A.

Pokud ale vložíte definovanou funkci B uvnitř funkce A:

function A() {

  var x = 42;

  function B() {
    console.log(x);
  }

}

// 42

Nyní máte "rozsah přístupu".

Funkce

V JavaScriptu spustíte funkci voláním:

function A() {
  console.log(42);
}

Takhle:

A();

// 42

Funkce jako hodnoty

V JavaScriptu můžete ukázat značku na funkci, stejně jako ukazuje na číslo:

var a = function() {
  console.log(42);
};

Proměnná a nyní znamená funkci, můžete ji spustit.

a();
// 42

Tuto proměnnou můžete také předat:

setTimeout(a, 1000);

Za sekundu (1000 milisekund) se funkce a nazývá:

// 42

Rozsah uzavření

Když definujete funkce, tyto funkce mají přístup k jejich vnějším rozsahům.

Když předáváte funkce kolem hodnot, bylo by problematické, kdyby byl přístup ztracen.

V JavaScriptu mají funkce přístup k vnějším proměnným rozsahu. Dokonce i když jsou projížděni, aby běhali někam jinam.

var a = function() {

  var text = 'Hello!'

  var b = function() {
    console.log(text);
    // inside function `b`, you have access to `text`
  };

  // but you want to run `b` later, rather than right away
  setTimeout(b, 1000);

}

Co se stane teď?

// 'Hello!'

Nebo zvažte toto:

var c;

var a = function() {

  var text = 'Hello!'

  var b = function() {
    console.log(text);
    // inside function `b`, you have access to `text`
  };

  c = b;

}

// now we are out side of function `a`
// call `a` so the code inside `a` runs
a(); 

// now `c` has a value that is a function
// because what happened when `a` ran

// when you run `c`
c();

// 'Hello!'

Stále můžete přistupovat k proměnným v rozsahu uzavření.

Přestože a je spuštěno a nyní používáte c mimo a.

To, co se zde právě stalo, se nazývá ' uzavření ' v JavaScriptu.

7
David Rosson

Uzavření není těžké pochopit. Záleží jen na pohledu.

Já je osobně rád používám v případech každodenního života.

function createCar()
{
    var rawMaterial = [/* lots of object */];
    function transformation(rawMaterials)
    {
       /* lots of changement here */
       return transformedMaterial;
    }
    var transformedMaterial = transformation(rawMaterial);
    function assemblage(transformedMaterial)
    {
        /*Assemblage of parts*/
        return car;
    }
    return assemblage(transformedMaterial);
}

V určitých případech musíme projít jen určitými kroky. Pokud jde o transformaci materiálů, je to užitečné pouze tehdy, když máte díly.

7
Alexis

Kdysi tam byl barbar

function caveman {

kdo měl velmi zvláštní skálu,

var rock = "diamond";

Nemohli jste se dostat do skály sami, protože to bylo v jeskyni v jeskyni. Pouze jeskynní věděl, jak najít a dostat skálu.

return {
    getRock: function() {
        return rock;
    }
};
}

Naštěstí byl přátelský jeskynní muž, a kdybyste byli ochotni počkat na jeho návrat, rád by ho za vás dostal.

var friend = caveman();
var rock = friend.getRock();

Docela chytrý barbar.

7
NinjaBeetle

Začněme zde, jak je definováno na MDN: Uzávěry jsou funkce, které odkazují na nezávislé (volné) proměnné (proměnné, které jsou používány lokálně, ale jsou definovány v uzavřeném rozsahu). Jinými slovy, tyto funkce „zapamatují“ prostředí, ve kterém byly vytvořeny.

Lexikální rozsah
Zvažte následující:

function init() {
  var name = 'Mozilla'; // name is a local variable created by init
  function displayName() { // displayName() is the inner function, a closure
    alert(name); // use variable declared in the parent function    
  }
  displayName();    
}
init();

init () vytvoří lokální proměnnou nazvanou název a funkci nazvanou displayName (). Funkce displayName () je vnitřní funkce, která je definována uvnitř init () a je dostupná pouze v těle funkce init (). Funkce displayName () nemá vlastní lokální proměnné. Protože však vnitřní funkce mají přístup k proměnným vnějších funkcí, displayName () může přistupovat k názvu proměnné deklarované v rodičovské funkci init ().

function init() {
    var name = "Mozilla"; // name is a local variable created by init
    function displayName() { // displayName() is the inner function, a closure
        alert (name); // displayName() uses variable declared in the parent function    
    }
    displayName();    
}
init();

Spusťte kód a všimněte si, že příkaz alert () v rámci funkce displayName () úspěšně zobrazuje hodnotu proměnné name, která je deklarována v nadřazené funkci. Toto je příklad lexikálního určování rozsahu, který popisuje, jak analyzátor řeší názvy proměnných při vnořených funkcích. Slovo "lexikální" odkazuje na skutečnost, že lexikální rozsah používá umístění, kde je proměnná deklarována v rámci zdrojového kódu, aby určila, kde je tato proměnná k dispozici. Vnořené funkce mají přístup k proměnným deklarovaným v jejich vnějším rozsahu.

Uzavření
Nyní zvažte následující příklad:

function makeFunc() {
  var name = 'Mozilla';
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();

Spuštění tohoto kódu má přesně stejný účinek jako předchozí příklad funkce init () výše: tentokrát bude řetězec "Mozilla" zobrazen v okně upozornění JavaScriptu. Co je jiné - a zajímavé - je, že vnitřní funkce displayName () je vrácena z vnější funkce před provedením.

Na první pohled se může zdát, že tento kód stále funguje. V některých programovacích jazycích existují lokální proměnné v rámci funkce pouze po dobu provádění této funkce. Jakmile makeFunc () dokončí provádění, můžete očekávat, že proměnná názvu již nebude přístupná. Protože však kód stále funguje podle očekávání, není to zřejmě v případě jazyka JavaScript.

Důvodem je to, že funkce v JavaScriptu uzávěry. Uzavření je kombinací funkce a lexikálního prostředí, ve kterém byla tato funkce deklarována. Toto prostředí se skládá z libovolných lokálních proměnných, které byly v době vytvoření uzávěru v rozsahu. V tomto případě je myFunc odkazem na instanci funkce displayName vytvořené při spuštění makeFunc. Instance displayName udržuje odkaz na své lexikální prostředí, ve kterém existuje název proměnné. Z tohoto důvodu, když je vyvolán myFunc, název proměnné zůstává k dispozici pro použití a "Mozilla" je předán upozornění.

Zde je o něco zajímavější příklad - funkce makeAdder:

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

var add5 = makeAdder(5);
var add10 = makeAdder(10);

console.log(add5(2));  // 7
console.log(add10(2)); // 12

V tomto příkladu jsme definovali funkci makeAdder (x), která má jeden argument x a vrací novou funkci. Funkce, kterou vrací, má jeden argument, y a vrací součet x a y.

MakeAdder je v podstatě továrna na funkce - vytváří funkce, které mohou přidávat specifickou hodnotu jejich argumentu. Ve výše uvedeném příkladu používáme naši továrnu funkcí k vytvoření dvou nových funkcí - jedné, která přidává k jejímu argumentu 5, a jedné, která přidává 10.

add5 a add10 jsou obě uzávěry. Sdílejí stejnou definici těla funkce, ale ukládají různá lexikální prostředí. V lexikálním prostředí add5 je x 5, zatímco v lexikálním prostředí pro add10 x je 10.

Praktické uzávěry

Uzávěry jsou užitečné, protože umožňují spojit některá data (lexikální prostředí) s funkcí, která na těchto datech pracuje. To má zjevné paralely s objektově orientovaným programováním, kde nám objekty umožňují spojit některá data (vlastnosti objektu) s jednou nebo více metodami.

V důsledku toho můžete použít uzávěr kdekoli, kde byste mohli objekt normálně používat pouze jednou metodou.

Situace, kdy byste to chtěli udělat, jsou na webu obzvláště běžné. Hodně z kódu, který píšeme v JavaScriptu na frontě, je založeno na událostech - definujeme nějaké chování, pak jej připojíme k události, kterou spustil uživatel (např. Kliknutí nebo stisknutí klávesy). Náš kód je obecně připojen jako zpětné volání: jedna funkce, která je provedena v reakci na událost.

Předpokládejme například, že chceme přidat některá tlačítka na stránku, která upraví velikost textu. Jedním ze způsobů, jak toho dosáhnout, je určit velikost písma elementu těla v obrazových bodech a potom nastavit velikost ostatních prvků na stránce (například záhlaví) pomocí relativní jednotky em:

body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}

Naše interaktivní tlačítka velikosti textu mohou změnit vlastnost font-size elementu těla a úpravy budou zachyceny dalšími prvky na stránce díky relativním jednotkám. Zde je JavaScript:

function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

size12, size14 a size16 jsou nyní funkce, které změní velikost textu textu na 12, 14 a 16 pixelů. Můžeme je připojit k tlačítkům (v tomto případě odkazy) takto:

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>


function makeSizer(size) {
  return function() {
    document.body.style.fontSize = size + 'px';
  };
}

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);

document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;

pro více informací o uzávěrech navštivte odkaz na MDN

6
Alireza

V minulosti jsem je četl všechny a jsou velmi informativní. Někteří přicházejí velmi blízko k získání jednoduchého vysvětlení a pak se dostávají do komplexu nebo zůstávají abstraktní, porážejí účel a nedokážou ukázat velmi jednoduché použití v reálném světě.

Ačkoliv si všechny příklady a vysvětlení prošpikujete, máte dobrou představu o tom, jaké uzávěry jsou a nejsou prostřednictvím komentářů a kódů, byl jsem stále nespokojen s velmi jednoduchou ilustrací, která mi pomohla získat užitečnost uzávěrů, aniž by se to stalo tak složitým. Moje žena se chce naučit kódování a já jsem si myslela, že zde potřebuji ukázat nejen to, co, ale proč, a jak.

Nejsem si jistý, že to dostane šestiletý člověk, ale myslím si, že by to mohlo být o něco blíž k tomu, aby bylo možné ukázat jednoduchý případ v reálném světě, který by mohl být užitečný a snadno pochopitelný.

Jedním z nejlepších (nebo nejblíže k nejjednodušším) je vyprávění příkladu Morrisových uzávěrů pro figuríny.

Vezmeme-li koncept "SayHi2Bob" jen o jeden krok dále, demonstrujeme dvě základní věci, které můžete sbírat od čtení všech odpovědí:

  1. Uzávěry mají přístup k proměnným obsahujícím funkci.
  2. Uzávěry přetrvávají ve vlastním paměťovém prostoru (a jsou tedy užitečné pro všechny druhy oop-y instancí)

Prokázal jsem si to a ukázal jsem si to, udělal jsem si trochu housle:

http://jsfiddle.net/9ZMyr/2/

function sayHello(name) {
  var text = 'Hello ' + name; // Local variable
  console.log(text);
  var sayAlert = function () {
      alert(text);
  }
  return sayAlert;
}

sayHello(); 
/* This will write 'Hello undefined' to the console (in Chrome anyway), 
but will not alert though since it returns a function handle to nothing). 
Since no handle or reference is created, I imagine a good js engine would 
destroy/dispose of the internal sayAlert function once it completes. */

// Create a handle/reference/instance of sayHello() using the name 'Bob'
sayHelloBob = sayHello('Bob');
sayHelloBob();

// Create another handle or reference to sayHello with a different name
sayHelloGerry = sayHello('Gerry');
sayHelloGerry();

/* Now calling them again demonstrates that each handle or reference contains its own 
unique local variable memory space. They remain in memory 'forever' 
(or until your computer/browser explode) */
sayHelloBob();
sayHelloGerry();

To ukazuje oba základní pojmy, které byste měli získat o uzavření.

V jednoduchých termínech vysvětlit, proč je to užitečné, mám základní funkci, na kterou mohu vytvářet odkazy nebo úchyty, které obsahují jedinečná data, která v rámci této paměti přetrvávají. Nemusím přepsat funkci pokaždé, když chci říct něčí jméno. Zapouzdřila jsem tuto rutinu a učinila ji znovu použitelnou.

To mi vede přinejmenším k základním pojmům konstruktérů, oopových praktik, singletonů vs. instancí instancí s vlastními daty atd.

Začnete-li s tímto novinkou, můžete přejít na složitější objektové/členské volání a doufejme, že koncepty budou přenášeny.

6
williambq

Myslím si, že je cenné učinit krok zpět a prozkoumat obecnější pojem „uzavření“ - takzvaného „operátora spojení“.

V matematice, “spojit” operátora je funkce na částečně objednané množině, která vrátí nejmenší objekt větší než nebo se rovnat jeho argumentům. Ve symbolech se spojí [a, b] = d tak, že d> = a d> = b, ale neexistuje e tak, že d> e> = a nebo d> e> = b.

Takže spojení vám dává nejmenší věc "větší" než části.

Všimněte si, že rozsahy JavaScriptu jsou částečně uspořádané struktury. Tak, že existuje rozumná představa o spojení. Zvláště spojení oborů je nejmenší rozsah větší než původní rozsah. Tento rozsah se nazývá uzavření .

Takže uzavření proměnných a, b, c je nejmenší rozsah (v mřížce rozsahů pro váš program!), Který přináší a, b a c do rozsahu.

6
nomen

Nejjednodušší případ použití, který mohu vysvětlit JavaScript uzávěry je modul vzor. V modulu Pattern definujete funkci a zavoláte ji ihned poté, co se nazývá výraz Immedantly Invoked Function Expression (IIFE). Vše, co píšete uvnitř této funkce, má soukromý rozsah, protože je definováno uvnitř uzávěru , což vám umožňuje "simulovat" soukromí v JavaScriptu. Jako tak:

 var Closure = (function () {
    // This is a closure
    // Any methods, variables and properties you define here are "private"
    // and can't be accessed from outside the function.

    //This is a private variable
    var foo = "";

    //This is a private method
    var method = function(){

    }
})();

Pokud byste naopak chtěli vytvořit jednu nebo více proměnných nebo metod viditelných mimo uzávěrku, můžete je vrátit uvnitř objektu. Jako tak:

var Closure = (function () {
  // This is a closure
  // Any methods, variables and properties you define here are "private"
  // and can't be accessed from outside the function.

  //This is a private variable
  var foo = "";

  //This is a private method
  var method = function(){

  }

  //The method will be accessible from outside the closure
  return {
    method: method
  }

})();

Closure.method();

Doufám, že to pomůže. Pozdravy,

6
Javier La Banca

Slovo uzavření jednoduše odkazuje na to, že máte přístup k objektům (šestiletým: věcem), které jsou uzavřené (šestileté: soukromé) v rámci funkce (šestiletý box). I když je funkce (šestiletý: box) mimo rozsah (šestiletý: poslaný daleko).

6
cube

Uzavření v podstatě vytváří dvě věci: - funkci - soukromý rozsah, k němuž má přístup pouze tato funkce

Je to jako dát nějaký nátěr kolem funkce.

Takže pro šestiletého věku by to mohlo být vysvětleno analogií. Řekněme, že postavím robota. Ten robot může dělat mnoho věcí. Mezi nimi jsem naprogramoval, aby spočítal počet ptáků, které vidí na obloze. Pokaždé, když viděl 25 ptáků, měl by mi říct, kolik ptáků viděl od začátku.

Nevím, kolik ptáků viděl, pokud mi to neřekl. Jen on ví. To je soukromý prostor. To je v podstatě paměť robota. Řekněme, že jsem mu dal 4 GB.

Řekl mi, kolik ptáků viděl, je vrácená funkce. Také jsem to vytvořil.

Tato analogie je trochu sucky, ale někdo to může vylepšit.

6
Taye

Můj pohled na uzávěry:

Uzavření lze porovnat s knihou se záložkou na polici.

Předpokládejme, že jste si přečetli knihu a máte rádi nějakou stránku v knize. Do této stránky vložíte záložku, abyste ji mohli sledovat.

Po dokončení čtení knihy již knihu nepotřebujete, s výjimkou, že chcete mít přístup k této stránce. Mohl jste právě vystřihnout stránku, ale pak byste ztratili kontext příběhu. Takže knihu dáte zpět do poličky se záložkou.

To je podobné uzavření. Kniha je vnější funkcí a stránka je vaše vnitřní funkce, která se vrací z vnější funkce. Záložka je odkazem na vaši stránku a kontext příběhu je lexikální rozsah, který musíte zachovat. Knihovnička je sada funkcí, kterou nelze vyčistit ze starých knih, dokud nedržíte stránku.

Příklad kódu:

function book() {
   var pages = [....]; //array of pages in your book
   var bookMarkedPage = 20; //bookmarked page number
   function getPage(){
       return pages[bookMarkedPage];
   }
   return getPage;
}

var myBook = book(),
    myPage = myBook.getPage();

Když spustíte funkci book(), přidělujete paměť v zásobníku pro funkci, ve které se má spustit. Ale protože vrací funkci, nelze ji uvolnit, protože vnitřní funkce má přístup k proměnným z kontextu mimo ni, v tomto případě „stránky“ a „bookMarkedPage“.

Takže efektivní volání book() vrací odkaz na uzavření, tj. Nejen na funkci, ale odkaz na knihu a její kontext, tj. Odkaz na funkci getPage , stav stránek a bookMarkedPage proměnné.

Některé body, které je třeba zvážit:

Bod 1: Knihovnička, stejně jako zásobník funkcí, má omezený prostor, takže ji používejte moudře.

Bod 2: Přemýšlejte o tom, zda potřebujete držet celou knihu, když chcete sledovat pouze jednu stránku. Můžete uvolnit část paměti tím, že neuložíte všechny stránky v knize při vrácení uzávěru.

Toto je můj pohled na Closures. Doufám, že to pomůže, a pokud si někdo myslí, že to není správné, dejte mi prosím vědět, protože mám velký zájem pochopit ještě více o oboru a uzávěrech!

5
poushy

Také ... Možná bychom měli vyříznout vašeho 27-letého přítele trochu slack, protože celá koncepce "uzávěrů" je opravdu je (!) .. . voodoo!

Tím mám na mysli: (a) nechcete, intuitivně očekávejte ... AND ... (b) když vám někdo vezme čas, aby vám to vysvětlil, vy určitě nečekejte, že to práce!

Intuice vám říká, že "to musí být nesmysl ... jistě musí to mít za následek nějakou syntaktickou chybu nebo něco!" Jak na Zemi (!) mohli byste ve skutečnosti "vytáhnout funkci z" středu "kdekoli-na-místě," tak, že byste mohli [ještě!] Skutečně mít přístup ke čtení/zápisu do kontext "kdekoli - to - byl - na ?!"

Když si konečně uvědomíte, že taková věc je možná, pak ... jistě ... kdokoli je po-skutečnost reakce by byla: "whoa-aaa (!) .. kew-el-lll ... (!!!) "

Nejdříve však bude "velká proti-intuitivní překážka". Intuice vám dává spoustu naprosto věrohodných očekávání, že taková věc by byla " samozřejmě, absolutně nesmyslná, a proto zcela nemožná."

Jak jsem řekl: "Je to voodoo."

4
Mike Robinson

Nejjednodušší, nejkratší a nejrozumnější odpověď:

Uzavření je blok kódu, kde každý řádek může odkazovat na stejnou sadu proměnných se stejnými názvy proměnných.

Pokud "toto" znamená něco jiného, ​​než dělá někde jinde, pak víte, že se jedná o dva různé uzávěry.

4
Nick Manning

Uzavření mohou být soukromé a veřejné proměnné nebo funkce.

var ClusureDemo = function() {
    //privare variables
    var localVa1, localVa2;

    //private functions
    var setVaOne = function(newVa) {
        localVa1 = newVa;
    },
    setVaTwo = function(newVa) {
        localVa2 = newVa;
    },
    getVaOne = function() {
        return localVa1;
    },
    getVaTwo = function() {
        return localVa2;
    };

    return {
        //public variables and functions
        outVaOne : localVa1,
        outVaTwo : localVa2,
        setVaOne : setVaOne,
        setVaTwo : setVaTwo,
        getVaOne : getVaOne,
        getVaTwo : getVaTwo
    };
};

//Test Demo
var app = new ClusureDemo();
app.outVaOne = 'Hello Variable One';
app.outVaTwo = 'Hello Variable Two';
app.setVaOne(app.outVaOne);
app.setVaTwo(app.outVaTwo);

alert(app.getVaOne());
alert(app.getVaTwo());

Demo

2
Pao Im

Uzavření je funkce, která má přístup k informacím z prostředí, ve kterém byla definována.

U některých je tato informace hodnota v prostředí v době vytvoření. Pro ostatní jsou informace proměnnými v prostředí v době vytvoření.

Pokud lexikální prostředí, k němuž se uzavření vztahuje, patří k funkci, která skončila, pak (v případě uzavření odkazujícího na proměnné v životním prostředí) budou tyto lexikální proměnné nadále existovat pro účely uzavření uzávěrky.

Uzavření lze považovat za zvláštní případ globálních proměnných - s privátní kopií vytvořenou právě pro tuto funkci.

Nebo to může být myšlenka jako metoda, kde je prostředí specifickou instancí objektu, jehož vlastnosti jsou proměnné v prostředí.

První (uzavření jako prostředí) podobné té druhé, kde je kopie prostředí kontextovou proměnnou předanou každé funkci v předchozím, a proměnné instance tvoří kontextovou proměnnou v této funkci.

Takže uzavření je způsob, jak volat funkci, aniž by bylo nutné explicitně specifikovat kontext jako parametr nebo jako objekt v vyvolání metody.

var closure = createclosure(varForClosure);
closure(param1);  // closure has access to whatever createclosure gave it access to,
                  // including the parameter storing varForClosure.

vs

var contextvar = varForClosure; // use a struct for storing more than one..
contextclosure(contextvar, param1);

vs

var contextobj = new contextclass(varForClosure);
contextobj->objclosure(param1);

Pro udržovatelný kód doporučuji objektově orientovaný způsob. Nicméně pro rychlý a snadný soubor úkolů (například vytvoření zpětného volání) se uzavření může stát přirozeným a jasnějším, zejména v kontextu lamda nebo anonymních funkcí.

2
Gerard ONeill

Uzavření je jednoduše tehdy, když funkce má přístup k jejímu vnějšímu rozsahu i po dokončení funkce oboru. Příklad:

function multiplier(n) {
    function multiply(x) {
          return n*x;
    }
    return mutliply;
}

var 10xmultiplier = multiplier(10);
var x = 10xmultiplier(5); // x= 50

můžeme vidět, že i po dokončení multiplikátoru vnitřní funkce násobí stále přístup k hodnotě x, která je v tomto příkladu 10.

Velmi obvyklé použití uzávěrů je currying (stejný příklad výše), kde naše funkce postupně kořeníme parametry namísto poskytování všech argumentů najednou.

Toho můžeme dosáhnout, protože Javascript (vedle prototypového OOP) umožňuje programovat funkčně, kde funkce vyššího řádu mohou mít jiné funkce jako argumenty (funkce třídy fisrt). funkční programování ve wikipedii

Vřele doporučuji, abyste si přečetli tuto knihu od Kyle Simpsona: 2 část knihy je věnována uzávěrkám a nazývá se rozsah a uzávěry. nevíte js: zdarma čtení na github

2
zak.http