# Domknięcia (Closures)

Spójrzmy na ten kod:

function outerFunc() {
  let outerVar = 'I am outside!';

  function innerFunc() {
    console.log(outerVar); // =>  "I am outside!"
  }

  innerFunc();
}
1
2
3
4
5
6
7
8
9

W zakresie (eng. scope) innerFunc mamy dostęp do outerVar dzięki leksykalnemu zakresowi (eng. lexical scope).

Zauważmy, że inwokacja innerFunc znajduje się w jej leksykalnym zakresie (zakres funkcji outerFunc).

Spróbujmy teraz wykonać innerFunc poza jej leksykalnym zakresem.

function outerFunc() {
let outerVar = 'I am outside!';

function innerFunc() {
console.log(outerVar); // => "I am outside!"
}

return innerFunc;
}

const myFunc = outerFunc();

myFunc(); // => "I am outside!"

1
2
3
4
5
6
7
8
9
10
11
12
13
14

Co ważne innerFunc nadal ma dostęp do outerVar z jej leksykalnego zakresu nawet gdy jest wywołana spoza niego.

# Przykłady:

# Event handler

let countClicked = 0;

myButton.addEventListener('click', function handleClick() {
  countClicked++;
  myText.innerText = `You clicked ${countClicked} times`;
});
1
2
3
4
5
6

Gdy naciskamy przycisk, handleClick jest wykonywana gdzieś w kodzie DOM. Wykonanie

dzieje się z dala od miejsca definicji. Lecz handleClick będąc domknięciem zapamiętuje conuntClicked i odświeża go przy każdym kliknięciu.

# Iterator funkcji forEach

let countEven = 0;
const items = [1, 5, 100, 10];

items.forEach(function iterator(number) {
  if (number % 2 === 0) {
    countEven++;
  }
});

countEven; // => 2
1
2
3
4
5
6
7
8
9
10

iterator jest domknięciem ponieważ zapamiętuje zmienną countEven.

# Currying

function multiply(a) {
  return function executeMultiply(b) {
    return a * b;
  }
}

const double = multiply(2);
double(3); // => 6
double(5); // => 10

const triple = multiply(3);
triple(4); // => 12
1
2
3
4
5
6
7
8
9
10
11
12

multiply jest currieowaną funkcją, która zwraca kolejną funkcję, currying jest możliwy dzięki domknięciom

executeMultiply(b) jest domknięciem, które zapamiętuje a ze swojego leksykalnego zakresu

i przy wykonaniu kalkuluje wartość a + b

# setTimeout

for(var i = 1; i < 5; i++){
    setTimeout(() => {

        console.log(i)

    }, i)
}
1
2
3
4
5
6
7

Celem jest wyświetlenie numerów 1, 2, 3 i 4.

Jednak wynikiem jest 4krotne wypisanie 5tki, występuje tutaj problem z domknięciem, zmienna i jest

zdefiniowana w pętli for i wszystkie wewnętrzne funkcje mają do niej dostęp więc po wykonaniu pętli

wszystkie wewnętrzne funkcje odwołują się do tej samej zmiennej i, która w ten czas ma wartość 5.

Możemy to rozwiązać zmieniając var na let lub jeśli nie mamy możliwości używania ES6

możemu użyć IIFE (immediately-invoked function expression).

for(var i = 1; i < 5; i++){
   (()=>{
     var  j = i;
     return setTimeout( () =>{
       console.log(j)
     }, j * 1000)
   })()
}
1
2
3
4
5
6
7
8

# Prywatność danych

Rozważmy kod:

function privateHolder(value){

  return {
    get: () => value
  }
}

const myHolder = privateHolder(10);

console.log(myHolder.get()) // => 10
1
2
3
4
5
6
7
8
9
10

W JavaScript, domknięcia są mechanizmem używanym do tworzenia prywatnych danych.

Przy użyciu domknięć, domknięte zmienne są dostępne jedynie w jej leksykalnym zakresie

i nie można się do nich dostać spoza tego zakresu bez użycia uprzywilejowanych metod jak np. get() w privateHolder().

# Kontekst wywołania (Execution context):

Kontekst wywołania to środowisko, którym wykonywany jest kod

JavaScriptowy, to w nim definiowane są wartości this, zmiennych, obiektów i funkcji.

# Rodzaje

Rozróżniamy 3 typy execution contextu w JavaScripcie:

  • Global execution context (GEC): Defaultowy EC tworzony w momencie gdy plik ładowany jest przez przeglądarkę.

    GEC zawiera cały kod, który nie znajduje się w środku funkcji i obiektów. Jako że silnik

    JS jest jednowątkowy istnieje tylko jeden GEC.

  • Functional execution context (FEC): Lokalny EC tworzony za każdym razem gdy wykonywana jest funkcja.

    Każda funkcja ma swój EC. Gdy wykonując GEC silnik JS napotka wywołanie funkcji

    stworzy nowy FEC dla tej funkcji.

  • Eval: Execution context w środku funkcji eval

@TODO add info about execution stack