---
title: Domknięcia
slug: Web/JavaScript/Domkniecia
translation_of: Web/JavaScript/Closures
---
<div> {{jsSidebar("Intermediate")}}</div>

<div></div>

<p class="summary">Domknięcie jest funkcją skojarzoną z odwołującym się do niej środowiskiem. Innymi słowy domknięcie daje Ci dostęp z funkcji wewnętrznej do zasięgu funkcji zewnętrznej.</p>

<h2 id="Zasięg_leksykalny">Zasięg leksykalny</h2>

<p>Rozważ poniższy przykład:</p>

<div>
<pre class="brush: js">function init() {
  var name = "Mozilla"; // name jest zmienną lokalną utworzoną przez funkcję init
  function displayName() { // displayName() jest wewnętrzną funkcją, domknięciem
    alert(name); // używa zmiennej zdeklarowanej w funkcji nadrzędnej
  }
  displayName();
}
init();</pre>
</div>

<p><code>init()</code> tworzy zmienną lokalną <code>name</code> oraz funkcję <code>displayName()</code>. <code>displayName()</code> jest funkcją lokalną która została zdefiniowana wewnątrz funkcji <code>init()</code> i jest dostępna tylko wewnątrz tej funkcji. <code>displayName()</code> nie ma własnych zmiennych lokalnych. Jednakże, ponieważ wewnętrzne funkcje mają dostęp do zmiennych zdefiniowanych w funkcjach zewnętrznych, <code>displayName()</code> ma dostęp do zmiennej <code>name</code> zdeklarowanej w funkcji nadrzędnej, <code>init()</code>.</p>

<p>{{JSFiddleEmbed("https://jsfiddle.net/xAFs9/3/", "js,result", 200)}}</p>

<p>Uruchom kod i zauważ że <code>alert()</code> zawarty w funkcji <code>displayName()</code> wyświetlił wartość ze zmiennej <code>name</code>, która jest zdeklarowana w funkcji nadrzędnej. Jest to przykład <em>zasięgu leksykalnego</em>, który opisuje jak parser rozwiązuje zmienne kiedy funkcje są zagnieżdżone. Słowo "leksykalny" odnosi się do faktu że zasięg leksykalny używa lokalizacji zdefiniowania zmiennej w kodzie źródłowym aby określić gdzie ta zmienna jest dostępna. Zagnieżdżone funkcje mają dostęp do zmiennych zdeklarowanych w ich zewnętrznym zasięgu.</p>

<h2 id="Domknięcie">Domknięcie</h2>

<p>Teraz rozważmy następujący przykład:</p>

<pre class="brush: js">function makeFunc() {
  var name = "Mozilla";
  function displayName() {
    alert(name);
  }
  return displayName;
}

var myFunc = makeFunc();
myFunc();
</pre>

<p>Jeżeli uruchomisz ten kod przekonasz się że ma takie samo działanie jak poprzedni przykład z funkcją <code>init()</code>: tym razem wartość tekstowa "Mozilla" zostanie wyświetlona w alercie. To, co jest inne, - i interesujące - to to, że wewnętrzna funkcja <code>displayName()</code> została zwrócona z nadrzędnej funkcji przed jej wykonaniem.</p>

<p>Na pierwszy rzut oka może się wydawać nieintuicyjne, że kod nadal pracuje. W niektórych językach programowania zmienne lokalne znajdujące się w funkcji istnieją tylko przez czas trwania tej funkcji. W momencie gdy <code>makeFunc()</code> zostanie wykonana możesz oczekiwać, że zmienna nie będzie już dostępna. Jednakże, ponieważ w naszym przypadku kod nadal pracuje, jak widać nie dotyczy to języka JavaScript.</p>

<p>Spowodowane jest to tym, że omawiane funkcje przybierają w Javascript formę domknięć. <em>Domknięcie</em> jest kombinacją funkcji i leksykalnego środowiska w którym ta funkcja została zdeklarowana. To środowisko zawiera każdą zmienną lokalną która była w zasięgu w momencie kiedy domknięcie zostało stworzone. W tym przypadku, <code>myFunc</code> jest referencją do instancji funkcji <code>displayName</code> stworzonej w momencie działania <code>makeFunc</code>. Instancja <code>displayName</code> zarządza referencją do jej leksykalnego środowiska, w którym istnieje zmienna. Dlatego, kiedy <code>myFunc</code> jest uruchomiona, zmienna pozostaje dostępna do użycia i "Mozilla" może być przekazane do <code>alert</code>.</p>

<p>Poniżej znajduje się znacznie bardziej interesujący przykład — funkcja <code>makeAdder</code>:</p>

<pre><code>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</code></pre>

<p>W tym przykładzie, zdefiniowaliśmy funkcję <code>makeAdder(x)</code>, która pobiera argument, <code>x</code>, i zwraca nową funkcję.  Zwrócona funkcja pobiera argument, y, i zwraca sume x i y.</p>

<p>W zasadzie <code>makeAdder</code> jest fabryką funkcji — wytwarza funkcje, które mogą dodawać pewną wartość do ich argumentu. W powyższym przykładzie używamy naszej fabryki funkcji do stworzenia dwóch nowych funkcji — jedna, która dodaje 5 do jej argumentu i druga, która dodaje 10.</p>

<p><code>add5</code> i <code>add10</code> są domknięciami. Dzielą ten sam kod zawarty w funkcji <code>makeAdder</code>, ale przechowują różne leksykalne środowisko. W leksykalnym środowisku <code>add5</code>, <code>x</code> wynosi 5, natomiast w leksykalnym środowisku <code>add10</code>, <code>x</code> jest równe 10.</p>

<h2 id="Praktyczne_domknięcia">Praktyczne domknięcia</h2>

<p>Domknięcia są przydatne, ponieważ pozwalają Ci powiązać część danych (środowisko leksykalne) z funkcją, która operuje na tych danych. Jest to oczywista analogia do programowania obiektowego, gdzie obiekty pozwalają nam na powiązanie części danych (właściwości obiektu) z jedną lub dwiema metodami.</p>

<p>W rezultacie możesz użyć dokmnięć w sytuacjach, gdzie normalnie byś użył/a obiektu z wyłącznie jedną metodą.</p>

<p>Potencjalne sytuacje zastosowania powyższego zachowania są szczególnie popularne w sieci. Wiele kodu, który piszemy we front-endowym Javascripcie bazuje na zdarzeniach — definiujemy jakieś zachowanie, następnie dołączamy je do wydarzenia, które jest wywoływane przez użytkownika (kliknięciem myszki lub naciśnięciem klawisza klawiatury). Nasz kod jest najczęściej dołączony jako callback (wywołanie zwrotne): pojedyncza funkcja wykonywana jako odpowiedź na wydarzenie.</p>

<p>Przyjmijmy przykładowo, że chcemy dodać do strony przyciski, które zmieniają wielkość tekstu. Jednym ze sposobów na osiągnięcie tego jest określenie rozmiaru czcionki <code>font-size</code> elementu <code>body</code> w pikselach, następnie ustawienie rozmiaru innych elementów na stronie (takich jak nagłówki) używając jednostki względnej <code>em</code>:</p>

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

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}</code></pre>

<p>Nasze interaktywne przyciski zmiany wielkości tekstu mogą zmienić wlaściwość <code>font-size</code> elementu <code>body</code>, a inne elementy strony dostosują się dzięki zastosowaniu jednostki względnej.</p>

<p>Poniżej realizacja w JavaScript:</p>

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

var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);</code></pre>

<p><code>size12</code>, <code>size14</code>, oraz <code>size16</code> są obecnie funkcjami, które zmienią rozmiar tekstu w <code>body</code> do odpowiednio 12, 14 oraz 16 pixeli. Możemy dołączyć je do przycisków (w tym przypadku linków) jak ponżej:</p>

<pre><code>document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;</code></pre>

<pre><code>&lt;a href="#" id="size-12"&gt;12&lt;/a&gt;
&lt;a href="#" id="size-14"&gt;14&lt;/a&gt;
&lt;a href="#" id="size-16"&gt;16&lt;/a&gt;</code></pre>

<p>{{JSFiddleEmbed("https://jsfiddle.net/vnkuZ/","","200")}}</p>

<h2 id="Emulowanie_prywatnych_metod_przy_użyciu_domknięć">Emulowanie prywatnych metod przy użyciu domknięć</h2>

<p>Języki takie jak Java dostarczają możliwość zadeklarowania metody jako prywatna, co oznacza, że może ona zostać wywołana wylącznie przez inne metody w tej samej klasie.</p>

<p>JavaScript nie zapewnia do tego wbudowanej metody, jednakże jest możliwa emulacja prywatnych metod przy użyciu domknięć. Prywatne metody nie sa wyłącznie użyteczne z racji możliwości ograniczenia dostępu do kodu: dają również świetną możliwość zarządzania Twoją globalną przestrzenią nazw (namespace) uniemożliwiając nieistotnym metodom zaśmiecenie interfejsu publicznego Twojego kodu.</p>

<p>Poniższy kod ukazuje, w jaki sposób można użyć domknięć do zdefiniowania publicznych funkcji, które mają dostęp do prywatnych funkcji i zmiennych. Używanie dokmnięć w taki sposób znane jest jako <a href="http://www.google.com/search?q=javascript+module+pattern" title="http://www.google.com/search?q=javascript+module+pattern">module pattern</a>:</p>

<pre><code>var counter = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();

console.log(counter.value()); // logs 0
counter.increment();
counter.increment();
console.log(counter.value()); // logs 2
counter.decrement();
console.log(counter.value()); // logs 1</code></pre>

<p>W poprzednich przykładach każde domknięcie miało własne leksykalne środowisko. Jednakże w tym przypadku tworzymy pojedyncze środowisko leksykalne, współdzielone przez trzy funkcje: <code>counter.increment</code>, <code>counter.decrement<font face="Open Sans, arial, x-locale-body, sans-serif"><span style="background-color: #ffffff;"> oraz </span></font></code><code>counter.value</code>.</p>

<p>Owo współdzielone środowisko leksykalne tworzone jest w ciele funkcji anonimowej, która jest wykonana w momencie, gdy tylko zostanie zdefiniowana. Środowisko leksykalne zawiera dwa prywatne przedmioty: zmienną o nazwie <code>privateCounter</code> i funkcję o nazwie <code>changeBy</code>. Żaden z tych prywatnych przedmiotów nie może być wywołany bezpośrednio spoza funkcji anonimowej. Zamiast tego, muszą mieć być one wywołane poprzez trzy funkcje publiczne, które są zwracane z anonimowej klasy opakowującej (wrapper).</p>

<p>Te trzy funkcje publiczne to domknięcia, które współdzielą to samo środowisko. Dzięki JavaScriptowemu zakresowi leksykalnemu, każda z nich ma dostęp do zmiennej <code>privateCounter</code> oraz funkcji <code>changeBy.</code></p>

<p>Zauważysz, że definiujemy anonimową funkcję, która tworzy licznik, a następnie od razu ją wywołujemy i przypisujemy wynik do zmiennej <code>counter</code>. Moglibyśmy przetrzymywać tę funkcję w oddzielnej zmiennej <code>makeCounter</code> i użyć jej do stworzenia kilku liczników.</p>

<pre><code>var makeCounter = function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  }
};

var counter1 = makeCounter();
var counter2 = makeCounter();
alert(counter1.value()); /* Alerts 0 */
counter1.increment();
counter1.increment();
alert(counter1.value()); /* Alerts 2 */
counter1.decrement();
alert(counter1.value()); /* Alerts 1 */
alert(counter2.value()); /* Alerts 0 */</code></pre>

<p>Zauważ, że każdy z dwóch liczników, <code>counter1</code> oraz <code>counter2</code>, jest niezależny od drugiego. Każde domknięcie odnosi się do innej wersji zmiennej <code>privateCounter</code> przez własne domknięcie. Za każdym razem gdy któryś z liczników jest wywołany, jego środowisko leksykalne zmienia się przez zmianę wartości tej zmiennej; jednakże zmiany wartości zmiennej jednego domknięcia nie wpływają na wartość w innym domknięciu.</p>

<p>Używanie domknięć w ten sposób dostarcza wielu korzyści, które normalnie kojarzone sa z programowaniem obiektowym — w szczególności ukrywaniem oraz enkapsulacją danych.</p>

<h2 id="Tworzenie_domknięć_w_pętlach_popularne_błędy">Tworzenie domknięć w pętlach: popularne błędy</h2>

<p>W czasach przed wprowadzeniem definicji <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let" title="let"><code>let</code> </a>w standardzie ECMAScript 2015, popularnym problemem z domknięciami było ich użycie w pętlach.<br>
 Rozważmy poniższy kod:</p>

<pre class="brush: html"><code>&lt;p id="help"&gt;Helpful notes will appear here&lt;/p&gt;
&lt;p&gt;E-mail: &lt;input type="text" id="email" name="email"&gt;&lt;/p&gt;
&lt;p&gt;Name: &lt;input type="text" id="name" name="name"&gt;&lt;/p&gt;
&lt;p&gt;Age: &lt;input type="text" id="age" name="age"&gt;&lt;/p&gt;</code></pre>

<pre class="brush: js"><code>function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i &lt; helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();</code></pre>

<p>{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/", "", 200)}}</p>

<p>Tablica <code>helpText</code> definiuje trzy podpowiedzi. Każda z nich jest powiązana z ID inputu z dokumentu. Następnie w pętli <code>for</code> dodawana jest obsługa zdarzenia <code>onfocus</code>, która ma pokazać tekst podpowiedzi podczas ustawienia focusa na pole.<br>
 <br>
 Jeżeli sprawdzisz działanie tego kodu okaże się, że nie działa tak, jak się spodziewaliśmy. Zawsze zostanie wyświetlona podpowiedź dotycząca ostatniego pola (<code>'Your age (you must be over 16)'</code>).</p>

<p>Powodem takiego działania funkcji zwrotnej obsługującej <code>onfocus</code> jest domknięcie. Składa sie ono z definicji funkcji i przechwyconego kontekstu zakresu z <code>setupHelp</code>. Trzy domknięcia stworzone są w pętli, ale dzielą one to samo środowisko leksykalne, ktore posiada zmienną, która jest aktualizowana (<code>item.help</code>). Wartości <code>item.help</code> są determinowane w pętli, więc kiedy obsługa zdarzenia <code>onfocus</code> zostanie wywołana, <code>item.help</code> będzie miało wartość z ostatniej iteracji pętli.</p>

<p>Rozwiązaniem tego problemu jest użycie kolejnych domknięć, szczególnie fabryki funkcji opisanej wcześniej:</p>

<pre class="brush: js"><code>function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i &lt; helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();</code></pre>

<p>{{JSFiddleEmbed("https://jsfiddle.net/v7gjv/1/", "", 300)}}</p>

<p>To rozwiązanie działa zgodnie z oczekiwaniami. W odróżnieniu od wcześniejszego przykładu, <code>makeHelpCallback</code> tworzy <em>nowe środowisko leksykalne</em> dla każdej funkcji zwrotnej, w której <code>help</code> odnosi się do odpowiadającego stringa z tablicy <code>helpText</code>.</p>

<p>Innym sposobem zapisu rozwiązania z anonimowymi domknięciami jest:</p>

<pre class="brush: js"><code>function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i &lt; helpText.length; i++) {
    (function() {
       var item = helpText[i];
       document.getElementById(item.id).onfocus = function() {
         showHelp(item.help);
       }
    })(); // Immediate event listener attachment with the current value of item (preserved until iteration).
  }
}

setupHelp();</code></pre>

<p>Jeżeli nie chcesz używać domknięć możesz użyć słowa kluczowego <code>let</code> ze standardu ES2015:</p>

<pre class="brush: js"><code>function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i &lt; helpText.length; i++) {
    let item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp();</code></pre>

<p>Ten przykład używa <code>let</code> zamiast <code>var</code>, więc każde domknięcie wiąże się z blokowym zasięgiem funkcji, dlatego nie potrzeba żadnych dodatkowych domknięć.</p>

<p>Alternatywą może być użycie <code>forEach()</code>, by iterować po tablicy <code>helpText</code> i dodać listener dla każdego elementu <code>&lt;input&gt;</code>:</p>



<pre class="brush: js"><code>function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  helpText.forEach(function(text) {
    document.getElementById(text.id).onfocus = function() {
      showHelp(text.help);
    }
  });
}

setupHelp();</code></pre>



<h2 id="Wątpliwości_wydajnościowe">Wątpliwości wydajnościowe</h2>

<p>Niemądrze jest, aby niepotrzebnie tworzyć funkcje wewnątrz innych funkcji, jeżeli domnkięcia nie są wymagane w danej sytuacji, jako że odbije się to w negatywny sposób na wydajność skryptu, mierzoną poprzez czas wykonywania jak również i używaną pamięć.</p>

<p>Na przykład gdy tworzymy nowy obiekt/klasę, metody powinny być zwykle powiązane z prototypem obiektu zamiast definiowane w konstruktorze obiektu. Przyczyną jest to, że za każdym razem gdy konstruktor zostanie użyty, metody zostaną nadpisane (czyli przy każdym tworzeniu nowego obiektu).</p>

<p>Rozważmy następujący przykład:</p>

<pre><code>function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function() {
    return this.name;
  };

  this.getMessage = function() {
    return this.message;
  };
}</code></pre>

<p>Ponieważ poprzedni przykład nie wykorzystuje zalet, które płyną z wykorzystania domknięć, możemy to przepisać w następujący sposób:</p>

<pre><code>function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName: function() {
    return this.name;
  },
  getMessage: function() {
    return this.message;
  }
};</code></pre>

<p>Jednakże ponowne definiowanie prototypu nie jest rekomendowane. Poniższy przykład zamiast tego dodaje właściwości do istniejącego prototypu:</p>

<pre><code>function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function() {
  return this.name;
};
MyObject.prototype.getMessage = function() {
  return this.message;
};</code></pre>

<p>Powyższy przykład może też zostać przepisany w bardziej czytelny sposób, z identycznym wynikiem:</p>

<pre><code>function MyObject(name, message) {
    this.name = name.toString();
    this.message = message.toString();
}
(function() {
    this.getName = function() {
        return this.name;
    };
    this.getMessage = function() {
        return this.message;
    };
}).call(MyObject.prototype);</code></pre>

<p>W poprzednich trzech przykładach odziedziczony prototyp może być współdzielony przez wszystkie obiektu i definicje metod nie muszą występować przy każdorazowym tworzeniu obiektu. Aby dowiedzieć się więcej, zobacz Szczegóły modelu obiektowego.</p>

<p>In the three previous examples, the inherited prototype can be shared by all objects and the method definitions need not occur at every object creation. See <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Details_of_the_Object_Model">Details of the Object Model</a> for more.</p>