Zakres zmiennych globalnych i lokalnych

Z tego artykułu dowiesz się, czym jest zakres i jak działa on w kodzie JavaScript.

Zakres to podstawowe pojęcie w języku JavaScript i innych językach programowania. Określa ono kontekst, w którym zmienne są pobierane i używane. Gdy będziesz uczyć się języka JavaScript i korzystać ze zmiennych, stanie się on bardziej przydatny i lepszy w odniesieniu do Twojego kodu.

Zakres może pomóc Ci:

  • Efektywniej wykorzystuj pamięć: Zakres umożliwia ładowanie zmiennych tylko wtedy, gdy jest to konieczne. Jeśli zmienna jest poza zakresem, nie musisz udostępniać jej kodowi, który jest obecnie wykonywany.
  • Łatwiejsze wykrywanie i naprawianie błędów: izolowanie zmiennych z zakresem lokalnym ułatwia rozwiązywanie problemów z kodem, ponieważ w przeciwieństwie do zmiennych globalnych masz pewność, że kod z zewnętrznego zakresu nie może modyfikować zmiennych o zakresie lokalnym.
  • Twórz małe bloki kodu wielokrotnego użytku: możesz na przykład napisać czystą funkcję, która nie opiera się na zakresie zewnętrznym. Taką funkcję możesz łatwo przenieść w inne miejsce, wprowadzając niewielkie zmiany.

Co to jest zakres?

Zakres zmiennej określa, w którym miejscu w kodzie możesz jej użyć.

JavaScript definiuje zmienne o zakresie globalnym lub lokalnym:

  • Zmienne z zakresem globalnym są dostępne ze wszystkich innych zakresów w kodzie JavaScript.
  • Zmienne z zakresem lokalnym są dostępne tylko w określonym kontekście lokalnym i są tworzone przez słowa kluczowe, np. var, let i const. Jeśli do utworzenia zmiennej w funkcji używasz słów kluczowych var, let lub const, ma ona zakres lokalny.

W dalszej części tego artykułu omawiamy blok i zakres leksykalny:

  • Zmienne blokowania są dostępne lokalnie dla bloku, co zależy od położenia nawiasów klamrowych, w których zdefiniowano instrukcję block. Tylko zmienne zadeklarowane ze słowami kluczowymi let lub const mają zakres blokowy.
  • Zakres leksy wskazuje lokalizację, w której zmienna jest zadeklarowana w kodzie źródłowym, aby określić, gdzie jest ona dostępna. Używając funkcji zamknięć, dasz połączonej funkcji dostęp do zmiennych, do których odwołuje się zakres zewnętrzny znany jako środowisko leksykalne.

Po uzyskaniu dostępu do zmiennej w jej zakresie JavaScript zwraca jej przypisaną wartość lub w inny sposób powoduje błąd.

Aby zadeklarować zmienną:

  • Za pomocą słów kluczowych var, const lub let zadeklaruj zmienne o zakresie lokalnym lub globalnym.
  • Zadeklaruj zmienne ograniczone do bloku, używając słów kluczowych const lub let.

Gdy zadeklarujesz zmienną var w funkcji, deklaracja udostępni ją najbliższej funkcji zamykającej. Słowo kluczowe var nie może służyć do deklarowania zmiennych z zakresem blokowym.

Przykłady zakresów

Ten przykład przedstawia zakres globalny, ponieważ zmienna greeting jest zadeklarowana poza dowolną funkcją lub blokiem, przez co jej wartość jest dostępna dla całego kodu w bieżącym dokumencie:

const greeting = 'hello';
console.log(greeting); // 'hello'

W przykładzie o zakresie globalnym do zmiennej greeting przypisana jest wartość hello.

Ten przykład ilustruje zakres lokalny, ponieważ deklaruje w funkcji zmienną greeting ze słowem kluczowym let. Zmienna greeting jest zmienną o zakresie lokalnym i nie jest dostępna poza funkcją.

function greet() {
  let greeting = 'Hello World!';
  console.log(greeting);
}

Ten przykład przedstawia zakres blokowy, ponieważ deklaruje zmienną greeting w bloku, tak aby była ona dostępna tylko w nawiasach klamrowych:

if (true) {
   const greeting = 'hello';
}

console.log(greeting); // ReferenceError: greeting is not defined

Zwróć uwagę, że gdy funkcja console.log próbuje zwrócić wartość zmiennej greeting, JavaScript zwraca komunikat o błędzie ReferenceError zamiast oczekiwanego komunikatu hello. Dlaczego?

Zwracany jest błąd, ponieważ zmienna greeting ma zakres blokowy, a najbliższy blok jest częścią instrukcji warunkowej if. Poza blokiem nie możesz uzyskać dostępu do zmiennych let i const zadeklarowanych w bloku. Dzięki temu masz dostęp do zmiennej greeting tylko w nawiasach klamrowych, które określają zakres bloku.

Ten przykład naprawia błąd, ponieważ umieszcza metodę console.log(message) w nawiasach klamrowych. Zaktualizowany kod przenosi metodę console.log(message) do bloku.

if (true) {
   const greeting = 'hello';
   console.log(greeting);
}

Typy zakresu

Zakres globalny

Dostęp do zmiennych o zakresie globalnym można uzyskać z dowolnego miejsca w programie.

Weźmy na przykład plik HTML, który importuje 2 pliki JavaScript: file-1.js i file-2.js:

<script src="file-1.js"></script>
<script src="file-2.js"></script>

W tym przykładzie zmienna globalMessage ma zakres globalny i jest zapisana poza funkcją. Podczas uruchamiania i wykonywania można uzyskać dostęp do wartości zmiennej globalMessage z dowolnego miejsca w programie JavaScript.

Możesz zobaczyć zawartość plików file-1.js i file-2.js w tym fragmencie kodu. Zwróć uwagę na dostępność zmiennej globalMessage w obu plikach.

// file-1.js
function hello() {
    var localMessage = 'Hello!';
}

var globalMessage = 'Hey there!';

// file-2.js
console.log(localMessage); // localMessage is not defined
console.log(globalMessage); // Hey there!

Istnieje inny rodzaj zakresu, który nie jest często omówiony w tym artykule. Jeśli utworzysz zmienną w module JavaScriptu, ale poza funkcją lub blokiem, nie będzie ona miała zakresu globalnego, a tylko modułu. Zmienne z zakresem modułu są dostępne w dowolnym miejscu bieżącego modułu, ale nie są dostępne w innych plikach ani modułach. Aby udostępnić zmienną ograniczoną do modułu innym plikom, musisz ją wyeksportować z modułu, w którym została utworzona, a następnie import z modułu, który ma mieć dostęp do zmiennej.

Zakres lokalny i zakres funkcji

Gdy tworzysz zmienne w funkcji JavaScriptu ze słowami kluczowymi var, let lub const, są one lokalne dla funkcji, więc masz do nich dostęp tylko z poziomu funkcji. Zmienne lokalne są tworzone w momencie uruchomienia funkcji i są usuwane po zakończeniu jej wykonywania.

W tym przykładzie deklarowana jest zmienna total w funkcji addNumbers(). Dostęp do zmiennych a, b, i total możesz uzyskać tylko w funkcji addNumbers().

function addNumbers(a, b) {
    const total = a + b;
}

addNumbers(3, 4);

Do nadawania nazw zmiennym możesz używać słów kluczowych let i const. Gdy używasz słowa kluczowego let, JavaScript może aktualizować zmienną. W przypadku słowa kluczowego const zmienna pozostaje jednak bez zmian.

var variable1 = 'Declared with var';
var variable1 = 'Redeclared with var';
variable1; // Redeclared with var

let variable2 = 'Declared with let. Cannot be redeclared.';
variable2 = 'let cannot be redeclared, but can be updated';
variable2; // let cannot be redeclared, but can be updated

const variable3 = 'Declared with const. Cannot be redeclared or updated';
variable3; // Declared with const. Cannot be redeclared or updated

Zakres bloku

Blokady służą do grupowania pojedynczych instrukcji lub zestawu wyrażeń. Aby zadeklarować zmienną lokalną w zakresie blokowym, możesz użyć słów kluczowych const lub let. Pamiętaj, że słowa kluczowego var nie możesz używać do deklarowania zmiennych z zakresem blokowym.

Na przykład w tym bloku zakres zmiennej name i jej wartości "Elizabeth" jest zawarty w nawiasach klamrowych. Zmienne w zakresie bloku są niedostępne poza blokiem.

{
    const name = "Elizabeth";
}

Zmiennych ograniczonych do bloku możesz używać w instrukcjach if, for i while.

Zwróć uwagę na 2 pętle for w tym fragmencie kodu. Jedna pętla for używa słowa kluczowego var do zadeklarowania zmiennej inicjującej, która zwiększa się przez liczby 0, 1 i 2. Druga pętla for używa słowa kluczowego let do zadeklarowania zmiennej inicjującej.

for (var i = 0; i < 2; i++) {
    // ...
}

console.log(i); // 2

for (let j = 0; j < 2; j++) {
    // ...
}

console.log(j); // The j variable isn't defined.

W poprzednim przykładowym kodzie możesz zauważyć, że zmienna i w pierwszej pętli for wyciekła poza pętlę for i nadal zachowuje wartość 2, ponieważ słowo kluczowe var nie używa zakresu bloku. Problem został rozwiązany w drugiej pętli for, w której zmienna j zadeklarowana za pomocą słowa kluczowego let jest ograniczona do bloku pętli for i nie istnieje po zakończeniu pętli for.

Ponowne użycie nazwy zmiennej w innym zakresie

Zakres może wyizolować zmienną w obrębie funkcji nawet wtedy, gdy używasz tej samej nazwy zmiennej w innym miejscu.

Z tego przykładu dowiesz się, jak użycie zakresu pozwala ponownie używać tej samej nazwy zmiennej w różnych funkcjach:

function listOne() {
    let listItems = 10;
    console.log(listItems); // 10
}

function listTwo() {
   let listItems = 20;
   console.log(listItems); // 20
}

listOne();
listTwo();

Zmienne listItems w funkcjach listOne() i listTwo() mają przypisane oczekiwane wartości, więc nie mogą ze sobą kolidować.

Zamknięcie i zakres leksykalny

Zamknięcia odnoszą się do zamkniętej funkcji, w której funkcja wewnętrzna może uzyskać dostęp do zewnętrznego zakresu funkcji, nazywanego też środowiskiem leksycznym. Zatem w języku JavaScript używasz zamknięcia, aby umożliwić funkcji odwoływanie się do zewnętrznego środowiska leksycznego. W ten sposób kod wewnątrz funkcji może odwoływać się do zmiennych zadeklarowanych poza funkcją. Można tak zakodować łańcuch odniesień do zewnętrznych środowisk leksykalnych, aby funkcja była wywoływana przez funkcję, która z kolei jest wywoływana przez inną funkcję.

W tym przykładzie kod tworzy zamknięcie w środowisku leksykalnym, które jest tworzone przy wywoływaniu funkcji outer(), które zamyka się po zmiennej hello. W związku z tym zmienna hello jest używana w funkcji wywołania zwrotnego setTimeout.

function outer() {
    const hello = 'world';

    setTimeout(function () {
        console.log('Within the closure!', hello)
    }, 100);
}

outer();

W przypadku zakresu leksycznego zakres jest określany podczas kompilacji kodu źródłowego, a nie w czasie działania. Aby dowiedzieć się więcej o środowisku leksycznym, przeczytaj artykuł o zakresie leksykostycznego i zamknięciu.

Moduły

Moduły JavaScript pomagają uporządkować kod JavaScript. Prawidłowo używane zapewniają skuteczną strukturę bazy kodu i ułatwiają ponowne użycie kodu. Zamiast używać zmiennych globalnych do współdzielenia zmiennych w różnych plikach, moduły JavaScript pozwalają na eksportowanie i import zmiennych.

// hello.js file
function hello() {
  return 'Hello world!';
}

export { hello };

// app.js file
import { hello } from './hello.js';

console.log(hello()); // Hello world!

Wersja demonstracyjna wizualizacji zakresu

Zakres to podstawowe pojęcie, które powinien znać każdy programista JavaScript. Aby lepiej zrozumieć system zakresów, możesz spróbować napisać własny kod za pomocą wizualizatora zakresu JS. W wersji demonstracyjnej kod jest używany za pomocą kolorów, aby ułatwić wizualizację zakresów JavaScript.

Podsumowanie

W tym artykule przedstawiamy różne typy zakresów. Zakres JavaScriptu należy do bardziej zaawansowanych pojęć związanych z programowaniem stron internetowych, dlatego dobrze ze przeczytaniem tej treści i zrozumieniem tego zagadnienia poświęcił dużo czasu.

Zakres nie jest funkcją widoczną dla użytkowników. Choć jest to problem tylko dla programisty stron internetowych, to wiedza o tym, jak działa zakres, może pomóc w naprawianiu ewentualnych błędów.