JavaScript przekazywanie wartości funkcjom
We wcześniejszym poście opisałem jak działa mechanizm zmiennych typu prostego i referencyjnego. Podkreśliłem na co należy uważać i czego trzeba unikać. W kolejnym poście opisałem jak radzić sobie z kopiowaniem tablic jeśli potrzebujemy tak zwanej „głębokiej kopii”.
Dziś napiszę o kolejnym ważnym aspekcie języka JavaScript, mającym związek z typami zmiennych. Jest to kolejna rzecz spędzająca sen z powiek początkującym programistom tego języka. Chodzi o przekazywanie typów referencyjnych i prosty jako argumenty funkcji.
JavaScript przekazywanie wartości funkcjom
Przekazywanie funkcji zmiennej jako argument powoduje, że funkcja tworzy kopie tej zmiennej. W przypadku gdy jest to zmienna typu prostego, kopiuje się wartość tej zmiennej. W przypadku zmiennej referencyjnej, kopiuje się referencja do wartości. To już wiemy, ale dla pełnej jasności spójrzmy na te dwa przykłady
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// String, zmienna typu prostego var pies = "Maja"; function pochwalPsa(dog) { dog += " - Dobry pies!"; return dog; } console.log(pochwalPsa(pies)); console.log(pies); //Wyniki w konsoli to: //>> Maja - Dobry pies! //>> Maja // Tablica, zmienna typu referencyjnego var psy = ["maja", "burek", "miluś", "rambo", "dżeki"]; function pochwalWielePsow(dogs) { for (var i=0; i<dogs.length; i++) { dogs[i] += " - Dobry pies!"; } return dogs; } console.log(pochwalWielePsow(psy)); console.log(psy); //Wyniki w konsoli to: // >> ["maja - Dobry pies!", "burek - Dobry pies!", "miluś› - Dobry pies!", "rambo - Dobry pies!", "dżeki - Dobry pies!"] // >> ["maja - Dobry pies!", "burek - Dobry pies!", "miluś› - Dobry pies!", "rambo - Dobry pies!", "dżeki - Dobry pies!"] |
Wszystko działa dokładnie tak jak się tego spodziewaliśmy. Zmiany na argumencie typu prostego, nie mają żadnego wpływu na oryginał. natomiast, zmiany na tablicy (typ referencyjny) są widoczne jeśli wywołamy jej wartość wcześniej stworzoną zmienną.
Ok, zobaczmy jak w tym wypadku zachowają się obiekty.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
function pies(name) { this.name = name; }; var maja = new pies("Maja"); function nagradzaniePsa(dogObj) { dogObj.name += " - Dobry pies!" return dogObj.name; } console.log(nagradzaniePsa(maja)); console.log(maja.name); //wyniki: // >> Maja - Dobry pies! // >> Maja - Dobry pies! |
Obiekt, to zmienna typu referencyjnego. Czyli tak jak zgadywaliśmy, przekazywanie go jako argumentu do funkcji, w której zmieniane są jego pola, zmieniają je również po za obszarem funkcji. Jeżeli tak nie zgadywałeś/aś to znaczy, że się nie przykładasz 🙂
Kolejny przykład
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function pies(name) { this.name = name; this.pochwal = function() { this.name += " - Dobry pies!" } }; function nagradzaniePsa(metoda) { metoda() } var maja = new pies("Maja"); nagradzaniePsa(maja.pochwal); console.log(maja.name); //wynik: // >> Maja |
Co ten JavaScript znów…
Przekazaliśmy obiekt jako argument, został zmieniony, a jak go wywołujemy, to zmian nie widać. Co się dzieje? Dlaczego czasem gdy przekazujemy obiekty to się zmieniają a czasem nie?
Odpowiedz może dla niektórych oczywista, ale ja miałem długo problem ze zrozumieniem co tu się wyczynia 🙂
Problemem tutaj jest słówko „this”, które wskazuje na aktualny kontekst wywołania. W przykładzie kontekstem nie jest obiekt a funkcja! Zmienione zostało pole „name” funkcji a nie obiektu. Niestety JavaScript nie poinformuje nas żadnym błędem. Jeżeli chcemy wykorzystać referencyjność obiektu musimy pamiętać o tym zjawisku.
No dobra, ale jak to obejść?
są na to sposoby. Jednym z nich jest użycie metody, przypisanej do każdej funkcji w JavaScripcie „call()”. W tej metodzie podajemy jako argument kontekst wywołania funkcji, przez co „this” wskazuje nam na dokładnie to co chcemy. Przykład:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
function pies(name) { this.name = name; this.pochwal = function() { this.name += " - Dobry pies!" } }; function nagradzaniePsa(metoda, obiekt) { metoda.call(obiekt); } var maja = new pies("Maja"); nagradzaniePsa(maja.pochwal, maja); console.log(maja.name); //wynik: // >> Maja - Dobry Pies! |
Sukces!