← Lekcje
JavaScript · lekcja 18

Funkcje w JavaScript 3/4:
co widzi funkcja - zmienne lokalne

Plan tematu (4 godziny):

  1. Godzina 1: własna funkcja - definicja, parametry, return
  2. Godzina 2: funkcja wywołana przez przycisk
  3. Godzina 3 (dziś): co widzi funkcja - zmienne lokalne
  4. Godzina 4: parseFloat, Math - powtórka
Rozgrzewka · 3 minuty

Rozgrzewka: funkcje z poprzedniej lekcji

  1. Jaki atrybut znacznika <button> wywołuje waszą funkcję po kliknięciu?
  2. Do czego służy document.getElementById("number1").value wewnątrz funkcji add()?
  3. Dlaczego funkcja absoluteValue(number) daje wynik 7 zarówno dla -7, jak i dla 7?
Część 1 · Zasięg zmiennej

Dwa zamknięte „pokoje”

zasięg globalny (poza funkcją) let counter = 100; wnętrze funkcji fun1 let i (z pętli for) a (parametr) mur bez let/var na zewnątrz nic tędy nie przejdzie

Tak jak w PHP, zmienna zadeklarowana let albo var wewnątrz funkcji istnieje tylko w jej „pokoju”. Kod na zewnątrz funkcji - poza parametrem (wejście) i return (wyjście) - jej nie widzi.

MDN: Learn_web_development/Core/Scripting/Functions, sekcja „Function scope and conflicts” („The top-level outside all your functions is called the global scope. Values defined in the global scope are accessible from everywhere in the code.” - tłum. własne: „Najwyższy poziom, poza wszystkimi funkcjami, nazywa się zasięgiem globalnym. Wartości zdefiniowane w zasięgu globalnym są dostępne z każdego miejsca kodu.”) oraz to samo źródło, porównanie z apartamentowcem: „Each apartment is private to the people who live there, similar to function scope... The building also may have common areas... This is like the global scope.” - tłum. własne: „Każde mieszkanie jest prywatne dla osób, które w nim mieszkają, podobnie jak zasięg funkcji... Budynek może też mieć części wspólne... To jest jak zasięg globalny.”
Część 2 · Zmienna z pętli for

let i w pętli: zamknięte w funkcji

function fun1(a) {
  for (let i = 1; i < 6; i++) {
    console.log(i * a);
  }
}
fun1(2);
console.log(i);   // poza funkcją

W konsoli: fun1(2) wypisuje kolejno 2 4 6 8 10. Linia console.log(i); po wywołaniu funkcji rzuca Uncaught ReferenceError: i is not defined - i istniało tylko w czasie działania fun1.

MDN: Learn_web_development/Core/Scripting/Functions, sekcja „Function scope and conflicts” („When you create a function, the variables and other things defined inside the function are inside their own separate scope. This means that they are locked away in their own separate compartment, unreachable from code outside the function.” - tłum. własne: „Gdy tworzysz funkcję, zmienne i inne rzeczy zdefiniowane wewnątrz niej znajdują się w swoim własnym, oddzielnym zasięgu. Są zamknięte w swoim własnym, oddzielnym pomieszczeniu, nieosiągalnym z kodu na zewnątrz funkcji.”)
Część 3 · Bez let ani var

Zmienna bez let/var „wycieka” na zewnątrz

wnętrze funkcji fun1 n = 1; (bez let, bez var) dziura w murze zasięg globalny (po fun1(2)) n = 6
function fun1(a) {
  for (n = 1; n < 6; n++) {
    console.log(n * a);
  }
}
fun1(2);
console.log(n);   // 6 - n istnieje na zewnątrz!

Ciekawostka z MDN: dokładnie ten błąd (literówka w nazwie zmiennej bez let) potrafił latami psuć duże programy, bo silnik JavaScript zawsze zgadzał się cicho utworzyć nową zmienną globalną. Tryb "use strict" to zmienia: przypisanie do niezadeklarowanej zmiennej od razu rzuca błąd, zamiast pozwolić kodowi „działać” dalej z ukrytym błędem.

Ten sam wzorzec (zmienna n bez var/let) pojawia się w zadaniu CKE E.14 czerwiec 2016 zad. 31, slajd „Tak pytają na egzaminie” · MDN, „Strict mode”, sekcja „Converting mistakes into errors”: „Strict mode makes it impossible to accidentally create global variables.” - tłum. własne: „Tryb ścisły uniemożliwia przypadkowe tworzenie zmiennych globalnych.”
Część 4 · Ta sama nazwa, dwie zmienne

Dwie funkcje, ta sama nazwa zmiennej - brak konfliktu

funkcja showMessage let total = 5; funkcja countGuests let total = 100; ta sama nazwa total, dwie różne zmienne - brak konfliktu
function showMessage() { let total = 5; console.log(total); }
function countGuests() { let total = 100; console.log(total); }
showMessage();    // 5
countGuests();    // 100
MDN: Learn_web_development/Core/Scripting/Functions, sekcja „Function scope and conflicts” - przykład z dwoma niezależnymi plikami, które definiują zmienną i funkcję o tej samej nazwie, ilustruje ten sam mechanizm zamykania zmiennych w osobnym zasięgu („Locking parts of your code away in functions avoids such problems and is considered a best practice.” - tłum. własne: „Zamykanie fragmentów kodu wewnątrz funkcji unika takich problemów i jest uznawane za dobrą praktykę.”)
Egzamin E.14

Tak pytają na egzaminie

1.

Dla dowolnego a z przedziału (0, 99) zadaniem funkcji zapisanej w języku JavaScript jest

function fun1(a) {
  for (n = a; n <= 100; n++)
    document.write(n);
  return n;
}
  1. zwrócenie liczb z przedziału a .. 99
  2. wypisanie liczb z przedziału a .. 99 i zwrócenie wartości 100
  3. wypisanie wartości zmiennej a oraz zwrócenie wartości zmiennej n
  4. wypisanie liczb z przedziału a .. 100 i zwrócenie wartości zmiennej n

2.

Jakie elementy wypisze funkcja printItems(2) stworzona w języku JavaScript?

function printItems(a) {
  for(var i=1;i<6;i++) {
    a++;
    if((a%2==0)||(a%3==0)) document.write(a+" ");
  }
}
  1. 6
  2. 2 3 4 6
  3. 3 4 6 8
  4. 3 4 6
CKE, E.14, czerwiec 2016, zadanie 31 · CKE, E.14, styczeń 2018, zadanie 34
Ćwiczenie przy komputerze · 25 minut

Ćwiczenie: co widzi fun1

  1. W swoim script.js z poprzednich lekcji dopisz function fun1(a) { for (let i = 1; i < 6; i++) { console.log(i * a); } }.
  2. Wywołaj fun1(2) i sprawdźcie w konsoli (F12) pięć wypisanych wyników.
  3. Zaraz pod wywołaniem dopiszcie console.log(i); - sprawdźcie, że i nie istnieje poza funkcją (Uncaught ReferenceError). Usuńcie tę linię po sprawdzeniu.
  4. Skopiujcie funkcję jako fun1NoLet i w pętli usuńcie słowo let przed i. Wywołajcie ją i sprawdźcie, że tym razem console.log(i); poza funkcją DZIAŁA - i „wyciekło” na zewnątrz.
  5. Dla chętnych w domu: wywołaj fun1(3) i sprawdź, czy poza funkcją nadal nie widać i.
Na koniec

Sprawdź się

  1. Dlaczego zmienna zadeklarowana let wewnątrz funkcji nie istnieje poza nią?
  2. Co się stanie, gdy w funkcji przypiszecie wartość zmiennej bez let ani var?
  3. Dlaczego dwie różne funkcje mogą mieć zmienną o tej samej nazwie bez konfliktu?

To tyle na dziś - kolejny temat: parseFloat i Math, powtórka funkcji.

Poza szkołą

A jak to wygląda w produkcji?

Na lekcjiW produkcjiStatus
let wewnątrz funkcji widoczne tylko lokalnieTen sam mechanizm w każdej funkcji JS; moduły ES dodatkowo izolują cały plik.nadal w użyciu
Zmienna bez let/var tworząca zmienną globalną (wyciek)"use strict" (domyślne w modułach ES) zamienia to w błąd zamiast cichego wycieku.zastępowane
Dwie funkcje z tą samą nazwą zmiennej lokalnejTen sam wzorzec pozwala pisać niezależne funkcje bez uzgadniania nazw zmiennych w całym projekcie.nadal w użyciu
MDN: Learn_web_development/Core/Scripting/Functions, sekcja „Function scope and conflicts” („JavaScript works like this mainly for security and organization. Sometimes you don't want variables to be accessible from everywhere in the code... This might be done maliciously or just by accident.” - tłum. własne: „JavaScript działa tak głównie ze względów bezpieczeństwa i organizacji. Czasem nie chcecie, żeby zmienne były dostępne z każdego miejsca kodu... Może się to zdarzyć złośliwie albo przez przypadek.”)