Zakres zmiennych globalnych i lokalnych

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

Zakres to podstawowa pojęcie w języku JavaScript i innych językach programowania, które określa kontekst, w którym używane są zmienne i ich używanie. W miarę nauki języka JavaScript i pracy ze zmiennymi staje się on bardziej przydatny i odpowiedni do zastosowania w kodzie.

Zakres pomaga:

  • Efektywniej korzystaj z pamięci: zakres umożliwia wczytywanie zmiennych tylko wtedy, gdy jest to potrzebne. Jeśli zmienna jest poza zakresem, nie musisz udostępniać jej dla obecnie wykonywanego kodu.
  • Łatwiejsze znajdowanie i naprawianie błędów: izolowanie zmiennych za pomocą zakresu lokalnego ułatwia rozwiązywanie problemów z kodem, ponieważ w przeciwieństwie do zmiennych globalnych możesz zaufać, że kod z zakresu zewnętrznego 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 wymaga zakresu zewnętrznego. Możesz łatwo przenieść taką funkcję w inne miejsce, wprowadzając niewielkie zmiany.

Co to jest zakres?

Zakres zmiennej określa, gdzie w kodzie możesz używać zmiennej.

JavaScript definiuje zmienne o zakresie globalnym lub lokalnym:

  • Zmienne o zakresie globalnym są dostępne we wszystkich innych zakresach w kodzie JavaScript.
  • Zmienne o zakresie lokalnym są dostępne tylko w określonym kontekście lokalnym i są tworzone na podstawie słów kluczowych, takich jak var, let i const. Jeśli do utworzenia zmiennej w funkcji używasz słów kluczowych var, let lub const, ta zmienna ma zakres lokalny.

W dalszych sekcjach tego artykułu omówiono zakres blokowy i leksykański:

  • Zmienne zakresu blokowania są dostępne lokalnie w bloku zgodnie z lokalizacją nawiasów klamrowych, w których zdefiniowano instrukcję bloku. Tylko zmienne zadeklarowane ze słowami kluczowymi let lub const mają zakres blokowy.
  • Zakres leksykański określa, gdzie jest ona dostępna, na podstawie lokalizacji, w której zmienna jest zadeklarowana w kodzie źródłowym. Zamknięcia służą do przyznawania zamkniętej funkcji dostępu do zmiennych, do których odwołuje się zakres zewnętrzny, zwanych środowiskiem leksycznym.

Gdy użytkownik uzyskuje dostęp do zmiennej w obrębie jej zakresu, JavaScript zwraca jej przypisaną wartość lub w inny sposób zwraca błąd.

Aby zadeklarować zmienną:

  • Do deklarowania zmiennych o zakresie lokalnym lub globalnym możesz używać słów kluczowych var, const lub let.
  • Do zadeklarowania zmiennych zakresu blokowego użyj słów kluczowych const lub let.

Gdy zadeklarujesz zmienną var w funkcji, deklaracja udostępnia tę zmienną najbliższą funkcji otaczającej. Nie możesz używać słowa kluczowego var do deklarowania zmiennych z zakresem blokowym.

Przykłady zakresów

Ten przykład pokazuje zakres globalny, ponieważ zmienna greeting jest zadeklarowana poza 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 zmienna greeting ma przypisaną wartość hello.

W tym przykładzie pokazano zakres lokalny, ponieważ deklaruje on zmienną greeting ze słowem kluczowym let w funkcji. 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 pokazuje zakres blokowy, ponieważ deklaruje zmienną greeting w bloku, tak aby zmienna była 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 wyświetlić 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. Do zmiennych let i const, które zadeklarujesz w bloku, nie będzie można uzyskać poza blokadą. Dlatego możesz uzyskać dostęp do zmiennej greeting tylko w nawiasach klamrowych, które określają zakres bloku.

Ten przykład naprawia błąd, ponieważ powoduje przeniesienie metody console.log(message) do nawiasów klamrowych. Zaktualizowany kod przenosi metodę console.log(message) do bloku kodu.

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

Typy zakresu

Zakres globalny

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

Rozważmy 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 została zapisana poza funkcją. Podczas uruchamiania i wykonywania możesz uzyskać dostęp do wartości zmiennej globalMessage z dowolnego miejsca w programie JavaScript.

W tym fragmencie kodu możesz zobaczyć zawartość plików file-1.js i file-2.js. 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 szczegółowo omawiany w tym artykule. Jeśli utworzysz zmienną w module JavaScriptu, ale poza funkcją lub blokiem, nie będzie ona miała zakresu globalnego, a jedynie zakres 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 potem import ją z modułu, który musi mieć do niej dostęp.

Zakres lokalny i zakres funkcji

Kiedy tworzysz zmienne w funkcji JavaScriptu ze słowami kluczowymi var, let lub const, są one generowane lokalnie, więc masz do nich dostęp wyłącznie z poziomu funkcji. Zmienne lokalne są tworzone po uruchomieniu funkcji i usuwane po zakończeniu jej wykonywania.

W tym przykładzie deklarowana jest zmienna total w funkcji addNumbers(). Zmienne a, b, oraz total są dostępne tylko w funkcji addNumbers().

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

addNumbers(3, 4);

Do nazywania zmiennych możesz używać słów kluczowych let i const. Jeśli używasz słowa kluczowego let, JavaScript może aktualizować zmienną. Jednak w przypadku słowa kluczowego const zmienna pozostaje stała.

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

Zablokuj zakres

Blokady służą do grupowania instrukcji lub ich zbioru. Do zadeklarowania zmiennej lokalnej z zakresem bloku możesz użyć słów kluczowych const lub let. Pamiętaj, że do deklarowania zmiennych z zakresem blokowym nie możesz używać słowa kluczowego var.

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

{
    const name = "Elizabeth";
}

Zmiennych ograniczonych do bloku możesz używać w instrukcjach if, for lub 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 inicjatora, która zwiększa się o liczby 0, 1 i 2. Druga pętla for używa słowa kluczowego let do deklarowania zmiennej inicjatora.

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 widać, ż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 zostaje rozwiązany w drugiej pętli for, w której zmienna j zadeklarowana ze słowem kluczowym let jest ograniczona do bloku pętli for i nie występuje po zakończeniu pętli for.

Ponowne wykorzystanie nazwy zmiennej w innym zakresie

Zakres może izolować zmienną w funkcji, nawet jeśli używasz tej samej nazwy zmiennej w innym miejscu w innym zakresie.

Ten przykład pokazuje, jak za pomocą zakresu można ponownie wykorzystać tę samą nazwę 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 kolidują ze sobą.

Zamknięcie i zakres leksykański

Zamknięcie to funkcja zamknięta, w której funkcja wewnętrzna może korzystać z zakresu funkcji zewnętrznej, nazywanego też środowiskiem leksycznym. Dlatego w JavaScripcie możesz stosować zamknięcia, by umożliwić funkcjom odwoływanie się do zewnętrznego środowiska leksykalnego, co umożliwia kodowanie wewnątrz zmiennych zadeklarowanych poza funkcją. Można dzięki temu zakodować łańcuch odniesień do zewnętrznych środowisk leksykacyjnych tak, aby funkcja była wywoływana przez funkcję, która z kolei jest wywoływana przez inną funkcję.

W tym przykładzie kod tworzy zakończenie środowiska leksykalnego tworzonego po wywołaniu funkcji outer(), które zamyka się na zmiennej hello. Dlatego 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 leksykalnego zakres jest określany podczas kompilacji kodu źródłowego, a nie w czasie działania. Więcej informacji o środowisku leksykańskim znajdziesz w artykule Zakres eksploracyjny i zamknięcie (w języku angielskim).

Moduły

Moduły JavaScript pomagają uporządkować kod JavaScript. Prawidłowo używane karty zapewniają efektywną strukturę bazy kodu i ułatwiają ponowne używanie kodu. Zamiast używać zmiennych globalnych do udostępniania zmiennych w różnych plikach, moduły JavaScript udostępniają metodę eksportowania 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 narzędzia do tworzenia zakresu

Zakres to podstawowa koncepcja, którą każdy programista JavaScript powinien zrozumieć. Aby lepiej zrozumieć system zakresu, możesz napisać własny kod za pomocą narzędzia JS Scope Visualizer. W wersji demonstracyjnej kod użyto zabarwień, aby ułatwić wizualizację zakresów JavaScript.

Podsumowanie

W tym artykule opisujemy różne typy zakresów. Zakres języka JavaScript to jedno z bardziej zaawansowanych pojęć związanych z tworzeniem stron internetowych, dlatego zachęcamy do przeczytania tej treści i poświęcenia czasu na jej zrozumienie.

Zakres nie jest funkcją widoczną dla użytkownika. Ma to wpływ tylko na programistę stron internetowych, który pisze kod, ale wiedza o tym, jak działa zakres, może pomóc w naprawianiu ewentualnych błędów.