# Metody obiektów, "this"

Obiekty zazwyczaj są tworzone po to, żeby przedstawiać rzeczywiste byty, takie jak użytkownicy, zadania do wykonania i tym podobne: 

```js
let user = {
  name: "John",
  age: 30
};
```

I tak jak w rzeczywistości, użytkownik może *działać*: wybrać coś z koszyka, zalogować się, wylogować itd.

Czynności w JavaScript'cie są funkcjami we właściwościach obiektu.

## Przykłady metod

Na początek, nauczmy użytkownika `user` przywitania się:

```js run
let user = {
  name: "John",
  age: 30
};

*!*
user.sayHi = function() {
  alert("Cześć!");
};
*/!*

user.sayHi(); // Cześć!
```

Właśnie stworzyliśmy funkcję za pomocą Wyrażenia Funkcji i przypisaliśmy ją do właściwości `user.sayHi` obiektu.

Następnie ją wywołaliśmy i nasz użytkownik potrafi teraz mówić!

Funkcję, która jest właściwością obiektu nazywamy *metodą*.

Także mamy tutaj metodę `sayHi` obiektu `user`.

Oczywiście, moglibyśmy również posłużyć się zadeklarowaną wcześniej funkcją jako metodą: 

```js run
let user = {
  // ...
};

*!*
// najpierw deklarujemy
function sayHi() {
  alert("Cześć!");
};

// następnie dodajemy jako metodę
user.sayHi = sayHi;
*/!*

user.sayHi(); // Cześć!
```

```smart header="Object-oriented programming"
Kiedy piszemy kod wykorzystujący obiekty do reprezentowania różnych istnień, nazywamy to [programowaniem obiektowym](https://pl.wikipedia.org/wiki/Programowanie_obiektowe), w skrócie:
"OOP".

OOP to bardzo rozległy i interesujący temat. Jak wybrać właściwe podmioty? Jak stworzyć zależności między nimi? Jest to cała architektura i istnieje wiele świetnych książek traktujących ten temat, jak np. "Wzorce projektowe. Elementy oprogramowania" autorstwa E.Gamma, R.Helm, R.Johnson, J.Vissides, lub  "Object-Oriented Analysis and Design with Applications" G.Booch, i wiele innych.
```
### Skrót składniowy dla metod

Istnieje skrócona składnia dla metod w literałach obiektowych:

```js
// poniższe obiekty działają tak samo

user = {
  sayHi: function() {
    alert("Cześć");
  }
};

// skrócona składnia wygląda lepiej, prawda ? 
user = {
*!*
  sayHi() { // to samo co "sayHi: function()"
*/!*
    alert("Cześć");
  }
};
```

Jak widzimy, możemy pominąć `"function"` i po prostu użyć `sayHi()`.

Prawde mowiąc, oba zapisy nie są całkowicie identyczne. Istnieją subtelne różnice między nimi, związane z dziedziczeniem (ten temat poruszymy później), ale na tem moment nie ma to znaczenia. W prawie każdym przypadku lepiej użyć krótszej składni.

## "this" w metodach

Często się zdarza, że metoda obiektu do poprawnego działania potrzebuje dostępu do informacji zawartej w tym samym obiekcie.

Dla przykładu, kod wewnątrz `user.sayHi()` może wymagać imienia użytkownika `user`.

**Aby zdobyć taki dostęp, metoda może wykorzystać słowo kluczowe `this`**

Wartością `this` jest obiekt "przed kropką", który został wykorzystany do wywołania metody.

Na przykład:

```js run
let user = {
  name: "John",
  age: 30,

  sayHi() {
*!*
    // "this" jest "aktualnym obiektem"
    alert(this.name);
*/!*
  }

};

user.sayHi(); // John
```

Podczas wywołania `user.sayHi()`, wartością `this` będzie `user`.

Możliwe jest również uzyskanie dostępu do obiektu bez używania `this`, przez odwołanie się do niego za pomocą zmiennej z zewnątrz:

```js
let user = {
  name: "John",
  age: 30,

  sayHi() {
*!*
    alert(user.name); // "user" zamiast "this"
*/!*
  }

};
```

...Jednak na takim kodzie nie można polegać. Jeśli skopiujemy obiekt `user` do innej zmiennej, np `admin = user` i zmienimy wartości w zmiennej `user`, wtedy nasza metoda będzie się odwoływać do niewłaściwego obiektu.

Taki przykład przedstawiono poniżej:

```js run
let user = {
  name: "John",
  age: 30,

  sayHi() {
*!*
    alert( user.name ); // pojawi się błąd
*/!*
  }

};


let admin = user;
user = null; // dla pewności nadpisujemy zmienną

admin.sayHi(); // Ups! wewnątrz sayHi(), wykorzystywana jest zła zmienna! Błąd!
```

Jeśli użylibyśmy `this.name` zamiast `user.name` wewnątrz `alert`, wtedy kod by zadziałał.

## "this" nie jest powiązane

W JavaScript słowo kluczowe `this` zachowuje się inaczej niż w innych językach programowania. Może ono być użyte w każdej funkcji.

Zapis taki jak w poniższym przykładzie nie powoduje błędu:

```js
function sayHi() {
  alert( *!*this*/!*.name );
}
```

Wartość `this` jest określana podczas wykonywania kodu, zależnie od kontekstu. 

Na przykład tutaj ta sama funkcja jest przypisana do dwóch różnych obiektów i posiada różne "this" przy wywoływaniach:

```js run
let user = { name: "John" };
let admin = { name: "Admin" };

function sayHi() {
  alert( this.name );
}

*!*
// używamy tej samej funkcji w obu obiektach
user.f = sayHi;
admin.f = sayHi;
*/!*

// wywołania mają różne this
// "this" wewnątrz funkcji jest obiektem "przed kropką"
user.f(); // John  (this == user)
admin.f(); // Admin  (this == admin)

admin['f'](); // Admin (kropka lub nawiasy kwadratowe udzielają dostępu do metody)
```

Zasada jest prosta: jeśli `obj.f()` jest wywołana, to `this` jest `obj` podczas wywoływania `f`. Więc w powyższym przykładzie jest to zarówno `user` lub `admin`.


````smart header="Calling without an object: `this` == undefined"
Możemy wywołać tę funkcję nawet bez obiektu:

```js run
function sayHi() {
  alert(this);
}

sayHi(); // undefined
```

W tym przypadku `this` jest `undefined` w trybie ścisłym. Jeśli spróbujemy uzyskać dostęp do `this.name` pojawi się błąd.

Poza trybem ścisłym, w tym przypadku, wartością `this` będzie *obiekt globalny* (`window` w przeglądarce, dojdziemy do tego w późniejszym rozdziale [](info:global-object)). Jest to zamierzchłe zachowanie języka, które tryb `"use strict"` naprawia.

Zazwyczaj takie wywołanie jest błędem w kodzie. Jeśli w funkcji istnieje `this`, to powinna ona zostać wywołana jako metoda obiektu.
````


```smart header="The consequences of unbound `this`"
Jeśli programujesz w innym języku, zapewne przywykłeś do "powiązanego this", gdzie metoda zdefiniowana w obiekcie zawsze posiada `this` wskazujące na ten obiekt.

W JavaScript `this` jest "wolne", jego wartość jest określana podczas wykonywania kodu i nie zależy od tego gdzie została zadeklarowana metoda, tylko jaki obiekt znajduje się "przed kropką".

Koncepcja ewaluacji `this` podczas wykonywania kodu ma wady i zalety. Z jednej strony, funkcja może być wykorzystywana przez różne obiekty. Z drugiej - im większa swoboda, tym większe ryzyko pomyłki.

Naszym zadaniem nie jest ocena czy taki wybór przy tworzeniu języka był dobry czy zły. Zastanawiamy się raczej jak z takim mechanizmem pracować, jakie zyskać dzięki temu korzyści i jak uniknąć problemów.
```

## Internals: Referencje

```warn header="Zaawansowane szczegóły języka"
Ta część zawiera bardziej zaawansowaną terminologię, pomagającą lepiej zrozumieć skrajne przypadki.

Jeśli chcesz szybciej przejść dalej, możesz pominąć tę część lub zostawić do przeczytania na później.
```

Zawiła metoda może doprowadzić do zgubienia `this`, na przykład: 

```js run
let user = {
  name: "John",
  hi() { alert(this.name); },
  bye() { alert("Pa!"); }
};

user.hi(); // John (zwykłe wywołanie działa bez problemu)

*!*
// teraz warunkowo wywołajmy metodę user.hi lub user.bye w zależności od wartości name
(user.name == "John" ? user.hi : user.bye)(); // Błąd!
*/!*
```

W ostatniej linijce operator warunkowy wybiera pomiędzy `user.hi` i `user.bye`. W powyższym przykładzie wynikiem jest `user.hi`.

Następnie metoda jest natychmiast wywoływana z nawiasami `()`. Ale nie działa prawidłowo!

Jak widzisz, wywołanie powoduje błąd, ponieważ wartość `"this"` wewnątrz metody staje się `undefined`.

Ten kod działa (obiekt kropka metoda):
```js
user.hi();
```

Ten nie działa (metoda ewaluowana):
```js
(user.name == "John" ? user.hi : user.bye)(); // Błąd!
```

Dlaczego? Jeśli chcemy zrozumieć dlaczego tak się dzieje, przyjrzyjmy się dokładnie jak działa wywołanie metody `obj.method()`.

Patrząc uważne, możemy zaobserwować dwie wykonujące się operacje w `obj.method()`:

1. Najpierw, kropka `'.'` pobiera właściwość `obj.method`.
2. Następnie nawiasy `()` ją wywołują. 

Jak więc informacja o `this` migruje z pierwszej części do drugiej?

Jeśli rozłożymy te operacje na oddzielne linie kodu, wartość `this` z pewnością zostanie zgubiona: 

```js run
let user = {
  name: "John",
  hi() { alert(this.name); }
}

*!*
// podział pomiędzy pobraniem i wywołanie metody na oddzielne linie
let hi = user.hi;
hi(); // Błąd, ponieważ this jest undefined
*/!*
```

`hi = user.hi` przypisuje metodę do zmiennej, a na samym końcu jest wywoływana jako osobna funkcja, więc `this` nie posiada już tutaj żadnej wartości.

**Żeby `user.hi()` działało prawidłowo, JavaScript używa sztuczki -- kropka `'.'` nie zwraca funkcji, tylko wartość ze specjalnym [Typem Referencji](https://tc39.github.io/ecma262/#sec-reference-specification-type).**

Typ Referencji jest "typem specyfikacji". Nie możemy go bezpośrednio uzyć, ale jest on wbudowany i wykorzystywany przez język.

Wartością Typu Referencji jest trójwartościowa kombinacja `(base, name, strict)`, gdzie:

- `base` jest obiektem.
- `name` jest nazwą właściwości.
- `strict` jest true jeśli używamy `use strict`.

Wynikiem dostępu do właściwości `user.hi` nie jest funkcja, tylko wartość Typu Referencji. Dla `user.hi` w trybie ścisłym jest to:

```js
// Reference Type value
(user, "hi", true)
```

Jeśli wywołujemy nawiasy `()` na Typie Referencji, otrzymują one całą informację o obiekcie, jego metodzie i mogą ustawić dla this prawidłową wartość (w tym przypadku `=user`).

Typ Referencji jest specjalnym "pośrednim" typem wewnętrznym, którego zadaniem jest przekazywanie informacji z kropki `.` do nawiasów `()`.

Każda inna operacja, jak przypisanie `hi = user.hi` odrzuca całkowicie Typ Referencji, bierze wartość z `user.hi` (funkcji) i przekazuje ją dalej. Zatem każda następna operacja "gubi" `this`.

Podsumowując, wartość `this` jest przekazywane we właściwy sposób jeśli funkcja jest wywoływana za pomocą kropki `obj.method()` lub nawiasów kwadratowych `obj[`method`]()` (obie składnie zadziałają tutaj identycznie). W dalszej części kursu, nauczymy się różnych możliwości aby rozwiązać ten problem, takich jak [func.bind()](/bind#solution-2-bind).

## Funkcje strzałkowe nie mają "this"

Funkcje strzałkowe są specjalnym typem funkcji: nie posiadają "własnego" `this`. Jeśli odnosimy się do `this` w takiej funkcji, jego wartość jest pobierana z zewnętrznej "normalnej" funkcji. 

W poniższym przykładzie `arrow()` używa `this` z zewnętrznej metody `user.sayHi()`:

```js run
let user = {
  firstName: "Ilya",
  sayHi() {
    let arrow = () => alert(this.firstName);
    arrow();
  }
};

user.sayHi(); // Ilya
```

Jest to specjalna właściwość funkcji strzalkowych, są użyteczne gdy nie chcemy mieć osobnego `this`, tylko wolimy je pobrać z zewnątrz. W późniejszym rozdziale <info:arrow-functions> zagłębimy się bardziej w to czym są funkcje strzałkowe. 


## Podsumowanie

- Funkcje które są przechowywane w obiekcie nazywamy "metodami".
- Metody pozwalają obiektom "zachowywać się" w sposób `object.zróbCoś()`
- Metody mają referencje do swojego obiektu, jest to ich wartość `this`

Wartość `this` jest określana podczas wykonywania kodu.
- Kiedy funkcja jest deklarowana, może ona użyć `this`, z tym że nie będzie ono miało wartości tak długo aż funkcja zostanie wywyłana.
- Jedna funkcja może być użwana jako metoda przez kilka obiektów.
- Kiedy funkcja jest wykonywana za pomocą składni: `object.method()`, `this` podczas wykonywania przybierze wartość `object`.

Zapamiętaj że funkcje strzałkowe są specjalnym typem funkcji: nie posiadają `this`. Kiedy chcemy uzyskać dostęp do `this` wewnątrz funkcji strzałkowej, wartość jest brana z zewnątrz.