Jaki jest zakres zmiennych w JavaScript?

2011

Jaki jest zakres zmiennych w javascript? Czy mają taki sam zakres wewnątrz, a nie poza funkcją? Czy to w ogóle ma znaczenie? Gdzie również przechowywane są zmienne, jeśli są zdefiniowane globalnie?

LYriCAlsSH
źródło
4
Oto kolejny fajny link do tego problemu: „ Objaśnienie zakresu i zamknięć JavaScript ”.
Inżynier oprogramowania w pełnym stosie
9
Oto artykuł, który bardzo ładnie to wyjaśnia. Wszystko, co musisz wiedzieć o zmiennym zakresie Javascript
Saurab Parakh
2
Wspomniany wcześniej e-book Kyle'a Simpsona jest dostępny do przeczytania na Githubie i zawiera wszystkie informacje na temat zakresów i zamknięć JavaScript. Można go znaleźć tutaj: github.com/getify/You-Dont-Know-JS/blob/master/… Jest to część serii książek „Nie znasz JS” , która jest świetna dla każdego, kto chciałby wiedzieć więcej o JavaScript.
3rik82

Odpowiedzi:

2534

TLDR

JavaScript ma leksykalny (zwany także statycznym) zakres i zamknięcie. Oznacza to, że możesz określić zakres identyfikatora, patrząc na kod źródłowy.

Cztery zakresy to:

  1. Globalny - widoczny przez wszystko
  2. Funkcja - widoczna w obrębie funkcji (oraz jej podfunkcji i bloków)
  3. Blok - widoczny w bloku (i jego podblokach)
  4. Moduł - widoczny w module

Poza specjalnymi przypadkami zakresu globalnego i zakresu modułu, zmienne deklarowane są za pomocą var(zakres funkcji), let(zakres bloków) i const(zakres bloków). Większość innych form deklaracji identyfikatora ma zakres blokowy w trybie ścisłym.

Przegląd

Zakres to region bazy kodowej, dla którego ważny jest identyfikator.

Środowisko leksykalne to odwzorowanie między nazwami identyfikatorów a powiązanymi z nimi wartościami.

Zakres jest utworzony z połączonego zagnieżdżenia środowisk leksykalnych, przy czym każdy poziom w zagnieżdżeniu odpowiada środowisku leksykalnemu kontekstu wykonania przodka.

Te połączone środowiska leksykalne tworzą „łańcuch” zakresu. Rozpoznawanie identyfikatora to proces wyszukiwania w tym łańcuchu pasującego identyfikatora.

Rozpoznawanie identyfikatora odbywa się tylko w jednym kierunku: na zewnątrz. W ten sposób zewnętrzne środowiska leksykalne nie mogą „widzieć” wewnętrznych środowisk leksykalnych.

Istnieją trzy istotne czynniki w podejmowaniu decyzji zakres danego identyfikatora w JavaScript:

  1. Jak zadeklarowano identyfikator
  2. Gdzie zadeklarowano identyfikator
  3. Niezależnie od tego, czy jesteś w trybie ścisłym, czy nie ścisłym

Niektóre sposoby deklarowania identyfikatorów:

  1. var, letaconst
  2. Parametry funkcji
  3. Parametr bloku połowu
  4. Deklaracje funkcji
  5. Nazwane wyrażenia funkcyjne
  6. Niejawnie zdefiniowane właściwości obiektu globalnego (tj. Brak varw trybie nieokreślonym)
  7. import sprawozdania
  8. eval

Niektóre identyfikatory lokalizacji można zadeklarować:

  1. Kontekst globalny
  2. Ciało funkcyjne
  3. Zwykły blok
  4. Góra struktury kontrolnej (np. Pętla, jeśli, podczas itp.)
  5. Korpus struktury kontrolnej
  6. Moduły

Style deklaracji

var

Identyfikatory zadeklarowane za pomocą var mają zakres funkcji , oprócz tego, gdy są zadeklarowane bezpośrednio w kontekście globalnym, w którym to przypadku są dodawane jako właściwości do obiektu globalnego i mają zasięg globalny. Istnieją osobne zasady ich używania w evalfunkcjach.

let i const

Identyfikatory zadeklarowane przy użyciu leti const mają zasięg blokowy , z wyjątkiem przypadków, gdy są zadeklarowane bezpośrednio w kontekście globalnym, w którym to przypadku mają zasięg globalny.

Uwaga: let, consti var wszyscy podnieśli . Oznacza to, że ich logiczna pozycja definicji jest górą ich obejmującego zakresu (bloku lub funkcji). Jednak zmienne zadeklarowano jako używane leti constnie można ich odczytać ani przypisać, dopóki kontrola nie przekroczy punktu deklaracji w kodzie źródłowym. Okres przejściowy jest znany jako czasowa martwa strefa.

function f() {
    function g() {
        console.log(x)
    }
    let x = 1
    g()
}
f() // 1 because x is hoisted even though declared with `let`!

Nazwy parametrów funkcji

Nazwy parametrów funkcji mają zasięg do treści funkcji. Zauważ, że jest to nieco skomplikowane. Funkcje zadeklarowane jako domyślne argumenty zamykają listę parametrów , a nie treść funkcji.

Deklaracje funkcji

Deklaracje funkcji mają zakres blokowy w trybie ścisłym, a zakres funkcji w trybie niewymaganym. Uwaga: tryb nie ścisły to skomplikowany zestaw nowych reguł opartych na dziwacznych historycznych implementacjach różnych przeglądarek.

Nazwane wyrażenia funkcyjne

Nazwane wyrażenia funkcyjne mają zasięg do siebie (np. W celu rekurencji).

Niejawnie zdefiniowane właściwości obiektu globalnego

W trybie nieokreślonym niejawnie zdefiniowane właściwości obiektu globalnego mają zasięg globalny, ponieważ obiekt globalny znajduje się na początku łańcucha zasięgu. W trybie ścisłym są one niedozwolone.

eval

W evalciągach zmiennych zadeklarowane za pomocą varzostaną umieszczone w bieżącym zakresie lub, jeśli evalsą używane pośrednio, jako właściwości obiektu globalnego.

Przykłady

Poniższa rzuci ReferenceError ponieważ nazwy x, ya znie mają żadnego znaczenia poza funkcją f.

function f() {
    var x = 1
    let y = 1
    const z = 1
}
console.log(typeof x) // undefined (because var has function scope!)
console.log(typeof y) // undefined (because the body of the function is a block)
console.log(typeof z) // undefined (because the body of the function is a block)

Poniższe spowoduje zgłoszenie błędu ReferenceError dla yi z, ale nie dla x, ponieważ widoczność xnie jest ograniczona przez blok. Bloki, które definiują ciała struktur kontrolnych if, fori whilezachowują się podobnie.

{
    var x = 1
    let y = 1
    const z = 1
}
console.log(x) // 1
console.log(typeof y) // undefined because `y` has block scope
console.log(typeof z) // undefined because `z` has block scope

Poniżej xwidoczny jest poza pętlą, ponieważ varma zasięg funkcji:

for(var x = 0; x < 5; ++x) {}
console.log(x) // 5 (note this is outside the loop!)

... z powodu tego zachowania należy zachować ostrożność przy zamykaniu zmiennych zadeklarowanych za pomocą varpętli. Jest xtu zadeklarowana tylko jedna instancja zmiennej , która logicznie leży poza pętlą.

Następujące wydruki 5, pięć razy, a następnie drukowane 5po raz szósty na console.logzewnątrz pętli:

for(var x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // closes over the `x` which is logically positioned at the top of the enclosing scope, above the loop
}
console.log(x) // note: visible outside the loop

Następujące drukuje undefinedponieważ xjest bloku o zakresie. Oddzwaniania są uruchamiane jeden po drugim asynchronicznie. Nowe zachowanie dla letzmiennych oznacza, że każda funkcja anonimowa zamknięty przez inną zmienną o nazwie x(inaczej byłoby to zrobić z var), a więc całkowite 0poprzez 4drukowane .:

for(let x = 0; x < 5; ++x) {
    setTimeout(() => console.log(x)) // `let` declarations are re-declared on a per-iteration basis, so the closures capture different variables
}
console.log(typeof x) // undefined

Poniższe NIE rzuci a, ReferenceErrorponieważ widoczność xnie jest ograniczona przez blok; zostanie jednak wydrukowane, undefinedponieważ zmienna nie została zainicjowana (z powodu ifinstrukcji).

if(false) {
    var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised

Zmienna zadeklarowana w górnej części forpętli za pomocą letjest kierowana do korpusu pętli:

for(let x = 0; x < 10; ++x) {} 
console.log(typeof x) // undefined, because `x` is block-scoped

Następujące wyrzuci a, ReferenceErrorponieważ widoczność xjest ograniczona przez blok:

if(false) {
    let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped

Zmienne zadeklarowane za pomocą var, letlub constsą zawężona do modułów:

// module1.js

var x = 0
export function f() {}

//module2.js

import f from 'module1.js'

console.log(x) // throws ReferenceError

Poniższe elementy zadeklarują właściwość obiektu globalnego, ponieważ zmienne zadeklarowane przy użyciu varw kontekście globalnym są dodawane jako właściwości do obiektu globalnego:

var x = 1
console.log(window.hasOwnProperty('x')) // true

leta constw kontekście globalnym nie dodawaj właściwości do obiektu globalnego, ale nadal mają zasięg globalny:

let x = 1
console.log(window.hasOwnProperty('x')) // false

Parametry funkcji można uznać za zadeklarowane w treści funkcji:

function f(x) {}
console.log(typeof x) // undefined, because `x` is scoped to the function

Parametry bloku połowowego mają zasięg do korpusu bloku połowowego:

try {} catch(e) {}
console.log(typeof e) // undefined, because `e` is scoped to the catch block

Nazwane wyrażenia funkcyjne mają zasięg tylko do samego wyrażenia:

(function foo() { console.log(foo) })()
console.log(typeof foo) // undefined, because `foo` is scoped to its own expression

W trybie nieokreślonym niejawnie zdefiniowane właściwości obiektu globalnego mają zasięg globalny. W trybie ścisłym pojawia się błąd.

x = 1 // implicitly defined property on the global object (no "var"!)

console.log(x) // 1
console.log(window.hasOwnProperty('x')) // true

W trybie ścisłym deklaracje funkcji mają zakres funkcji. W trybie ścisłym mają zakres blokowy.

'use strict'
{
    function foo() {}
}
console.log(typeof foo) // undefined, because `foo` is block-scoped

Jak to działa pod maską

Zakres jest zdefiniowany jako leksykalny region kodu, dla którego ważny jest identyfikator.

W JavaScript każda funkcja-obiekt ma ukryte [[Environment]]odniesienia, który stanowi odniesienie dla środowiska słownikowego z kontekstu wykonania (ramka stosu), w którym został utworzony.

Po wywołaniu funkcji wywoływana jest [[Call]]metoda ukryta . Ta metoda tworzy nowy kontekst wykonania i ustanawia połączenie między nowym kontekstem wykonania a środowiskiem leksykalnym obiektu funkcji. Robi to, kopiując [[Environment]]wartość na obiekcie funkcji do zewnętrznego pola referencyjnego w środowisku leksykalnym nowego kontekstu wykonania.

Zauważ, że to połączenie między nowym kontekstem wykonania a środowiskiem leksykalnym obiektu funkcji nazywa się zamknięciem .

Zatem w JavaScript zakres jest implementowany przez środowiska leksykalne połączone ze sobą w „łańcuch” zewnętrznymi odnośnikami. Ten łańcuch środowisk leksykalnych nazywany jest łańcuchem zasięgu, a rozpoznawanie identyfikatora następuje poprzez wyszukiwanie łańcucha w celu znalezienia pasującego identyfikatora.

Dowiedz się więcej .

Tryptyk
źródło
280
Nawet nie jest to wyczerpujące, ale być może jest to niezbędny zestaw sztuczek JavaScript, których potrzebujesz, aby skutecznie PRZECZYTAĆ nowoczesny JavaScript.
Tryptyk
148
Odpowiedź wysoko oceniona, nie wiem dlaczego. To tylko kilka przykładów bez odpowiedniego wyjaśnienia, a następnie wydaje się mylić dziedziczenie prototypu (tj. Rozdzielczość właściwości) z łańcuchem zakresu (tj. Rozdzielczość zmienna). Wyczerpujące (i dokładne) wyjaśnienie zakresu i rozdzielczości właściwości znajduje się w uwagach na temat FAQ comp.lang.javascript .
RobG
109
@RobG Jest wysoko oceniany, ponieważ jest użyteczny i zrozumiały dla szerokiej gamy programistów, niezależnie od drobnych katachrezy. Link, który zamieściłeś, chociaż jest przydatny dla niektórych profesjonalistów, jest niezrozumiały dla większości osób piszących dzisiaj Javascript. Możesz rozwiązać wszelkie problemy z nomenklaturą, edytując odpowiedź.
Tryptyk
7
@ tryptyk - edytuję odpowiedzi, aby naprawić drobne rzeczy, a nie najważniejsze. Zmiana „zakresu” na „właściwość” naprawi błąd, ale nie problem mieszania dziedziczenia i zakresu bez bardzo wyraźnego rozróżnienia.
RobG
24
Jeśli zdefiniujesz zmienną w zakresie zewnętrznym, a następnie masz instrukcję if, zdefiniuj zmienną wewnątrz funkcji o tej samej nazwie, nawet jeśli ta gałąź, jeśli nie zostanie osiągnięta , zostanie ponownie zdefiniowana. Przykład - jsfiddle.net/3CxVm
Chris S
233

JavaScript używa łańcuchów zasięgu do ustalenia zakresu dla danej funkcji. Zazwyczaj istnieje jeden zakres globalny, a każda zdefiniowana funkcja ma swój własny zakres zagnieżdżony. Każda funkcja zdefiniowana w innej funkcji ma zasięg lokalny, który jest powiązany z funkcją zewnętrzną. To zawsze pozycja w źródle określa zakres.

Elementem łańcucha zasięgu jest w zasadzie mapa ze wskaźnikiem do zakresu nadrzędnego.

Podczas rozwiązywania zmiennej javascript zaczyna się od najbardziej wewnętrznego zakresu i wyszukuje na zewnątrz.

krosenvold
źródło
1
Łańcuchy zakresów to kolejny termin na [ zamknięcie pamięci] ... dla osób czytających tutaj, aby uczyć się / czytać javascript.
Nowa Aleksandria
108

Zmienne deklarowane globalnie mają zasięg globalny. Zmienne zadeklarowane w ramach funkcji mają zasięg do tej funkcji i śledzą zmienne globalne o tej samej nazwie.

(Jestem pewien, że istnieje wiele subtelności, które prawdziwi programiści JavaScript będą w stanie wskazać w innych odpowiedziach. W szczególności natknąłem się na tę stronę o tym, co dokładnie thisoznacza w dowolnym momencie. Mam nadzieję, że ten bardziej wprowadzający link wystarczy, aby zacząć) .)

Jon Skeet
źródło
7
Boję się nawet zacząć odpowiadać na to pytanie. Jako prawdziwy programista JavaScript wiem, jak szybko odpowiedź mogła wymknąć się spod kontroli. Ładne artykuły.
Tryptyk
10
@Triptych: Wiem o co ci chodzi o sprawy wymykają się spod kontroli, ale proszę dodać odpowiedź tak. Mam wyżej tylko od robienia kilku wyszukiwań ... odpowiedź napisany przez kogoś z rzeczywistych doświadczeń jest zobowiązany być lepiej. Proszę poprawić moją odpowiedź, która jest zdecydowanie błędna!
Jon Skeet
4
W jakiś sposób Jon Skeet jest odpowiedzialny za MOJĄ najpopularniejszą odpowiedź na temat przepełnienia stosu.
Tryptyk
75

JavaScript w starej szkole

Tradycyjnie JavaScript ma tylko dwa typy zakresu:

  1. Globalny zakres : Zmienne są znane w całej aplikacji, od początku aplikacji (*)
  2. Zakres funkcjonalny : Zmienne są znane w ramach funkcji, w której są zadeklarowane, od początku funkcji (*)

Nie będę się tym zajmował, ponieważ istnieje już wiele innych odpowiedzi wyjaśniających różnicę.


Nowoczesny JavaScript

Te najnowsze specyfikacje JavaScript teraz również umożliwić trzeci zakres:

  1. Blok Zakres : Identyfikatory są „znane” z góry zakresu, w którym zostały zadeklarowane , ale nie można ich przypisać ani odnieść do nich (odczytać), dopóki nie znajdą się za wierszem ich deklaracji. Ten okres przejściowy nazywany jest „czasową martwą strefą”.

Jak utworzyć zmienne zakresu bloków?

Tradycyjnie tworzysz swoje zmienne w następujący sposób:

var myVariable = "Some text";

Zmienne zakresu bloków są tworzone w następujący sposób:

let myVariable = "Some text";

Jaka jest różnica między zakresem funkcjonalnym a zakresem blokowym?

Aby zrozumieć różnicę między zakresem funkcjonalnym a zakresem blokowym, rozważ następujący kod:

// i IS NOT known here
// j IS NOT known here
// k IS known here, but undefined
// l IS NOT known here

function loop(arr) {
    // i IS known here, but undefined
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( var i = 0; i < arr.length; i++ ) {
        // i IS known here, and has a value
        // j IS NOT known here
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here

    for( let j = 0; j < arr.length; j++ ) {
        // i IS known here, and has a value
        // j IS known here, and has a value
        // k IS known here, but has a value only the second time loop is called
        // l IS NOT known here
    };

    // i IS known here, and has a value
    // j IS NOT known here
    // k IS known here, but has a value only the second time loop is called
    // l IS NOT known here
}

loop([1,2,3,4]);

for( var k = 0; k < arr.length; k++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS NOT known here
};

for( let l = 0; l < arr.length; l++ ) {
    // i IS NOT known here
    // j IS NOT known here
    // k IS known here, and has a value
    // l IS known here, and has a value
};

loop([1,2,3,4]);

// i IS NOT known here
// j IS NOT known here
// k IS known here, and has a value
// l IS NOT known here

Tutaj możemy zobaczyć, że nasza zmienna jjest znana tylko w pierwszej pętli for, ale nie przed i po. Jednak nasza zmienna ijest znana w całej funkcji.

Należy również wziąć pod uwagę, że zmienne o zasięgu blokowym nie są znane przed zadeklarowaniem, ponieważ nie są podnoszone. Nie można również ponownie ustawić tej samej zmiennej o zasięgu blokowym w tym samym bloku. To sprawia, że ​​zmienne o zasięgu blokowym są mniej podatne na błędy niż zmienne o zasięgu globalnym lub funkcjonalnym, które są podnoszone i które nie powodują żadnych błędów w przypadku wielu deklaracji.


Czy bezpiecznie jest dziś używać zmiennych zakresu bloków?

To, czy korzystanie z niego jest obecnie bezpieczne, zależy od środowiska:

  • Jeśli piszesz kod JavaScript po stronie serwera ( Node.js ), możesz bezpiecznie użyć letinstrukcji.

  • Jeśli piszesz kod JavaScript po stronie klienta i używasz transpilatora opartego na przeglądarce (takiego jak Traceur lub Babel-standalone ), możesz bezpiecznie użyć tego letoświadczenia, jednak Twój kod prawdopodobnie nie będzie optymalny pod względem wydajności.

  • Jeśli piszesz kod JavaScript po stronie klienta i używasz transpilatora opartego na Węźle (takiego jak skrypt powłoki traceur lub Babel ), możesz bezpiecznie użyć letinstrukcji. Ponieważ Twoja przeglądarka będzie wiedziała tylko o transpilowanym kodzie, wady wydajności powinny być ograniczone.

  • Jeśli piszesz kod JavaScript po stronie klienta i nie używasz transpilatora, musisz rozważyć obsługę przeglądarki.

    Oto niektóre przeglądarki, które w ogóle nie obsługują let:

    • Internet Explorer 10 i poniżej
    • Firefox 43 i nowsze wersje
    • Safari 9 i niższe
    • Przeglądarka na Androida 4 i nowsze
    • Opera 27 i poniżej
    • Chome 40 i poniżej
    • DOWOLNA wersja przeglądarki Opera Mini i Blackberry

wprowadź opis zdjęcia tutaj


Jak śledzić obsługę przeglądarki

Aby letzapoznać się z aktualnym przeglądem przeglądarek obsługujących to oświadczenie podczas czytania tej odpowiedzi, zobacz Can I Usestronę .


(*) Zmienne o zasięgu globalnym i funkcjonalnym mogą być inicjowane i używane przed zadeklarowaniem, ponieważ zmienne JavaScript są podnoszone . Oznacza to, że deklaracje są zawsze na szczycie zakresu.

John Slegers
źródło
2
„NIE JEST znany” wprowadza w błąd, ponieważ zmienna jest tam zadeklarowana z powodu podnoszenia.
Oriol
Powyższy przykład wprowadza w błąd, zmienne „i” i „j” nie są znane poza blokiem. Zmienne „Let” mają zasięg tylko w tym konkretnym bloku, który nie znajduje się poza tym blokiem. Niech ma także inne zalety, nie można ponownie zmienić deklaracji zmiennej i zawiera ona zakres leksykalny.
zakir
1
To było pomocne, dzięki! Myślę, że jeszcze bardziej pomocne byłoby sprecyzowanie, co rozumiesz przez „Modern JavaScript” i „Old school JavaScript”; Myślę, że odpowiadają one odpowiednio ECMAScript 6 / ES6 / ECMAScript 2015 i wcześniejszym wersjom?
Jon Schneider
1
@JonSchneider: Poprawnie! Tam, gdzie mówię „old school JavaScript”, mówię o ECMAScript 5, a tam, gdzie mówię o „nowoczesnym JavaScript”, mówię o ECMAScript 6 (aka ECMAScript 2015). Nie sądziłem jednak, że tak ważne jest tutaj szczegółowe omówienie, ponieważ większość ludzi chce po prostu wiedzieć (1) jaka jest różnica między zakresem bloku a zakresem funkcjonalnym, (2) jakie przeglądarki obsługują zakres bloku i (3) czy można bezpiecznie używać zakresu bloków dzisiaj do każdego projektu, nad którym pracują. Dlatego skoncentrowałem się na rozwiązaniu tych problemów.
John Slegers
1
@JonSchneider: (ciąg dalszy) Niemniej jednak właśnie dodałem link do artykułu Smashing Magazine na temat ES6 / ES2015 dla tych, którzy chcą dowiedzieć się więcej o tym, jakie funkcje zostały dodane do JavaScript w ciągu ostatnich kilku lat ... każdego, kto może zastanawiam się, co mam na myśli przez „nowoczesny JavaScript”.
John Slegers
39

Oto przykład:

<script>

var globalVariable = 7; //==window.globalVariable

function aGlobal( param ) { //==window.aGlobal(); 
                            //param is only accessible in this function
  var scopedToFunction = {
    //can't be accessed outside of this function

    nested : 3 //accessible by: scopedToFunction.nested
  };

  anotherGlobal = {
    //global because there's no `var`
  }; 

}

</script>

Będziesz chciał zbadać zamknięcia i dowiedzieć się, jak z nich korzystać, aby tworzyć członków prywatnych .

geowa4
źródło
31

Kluczem, jak rozumiem, jest to, że JavaScript ma zakres funkcji na poziomie w porównaniu z bardziej powszechnym zasięgiem bloku C.

Oto dobry artykuł na ten temat.

James McMahon
źródło
26

W „Javascript 1.7” (rozszerzenie Mozilli do Javascript) można również zadeklarować zmienne o zasięgu blokowym za pomocą letinstrukcji :

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4
kennytm
źródło
2
Tak, ale czy można go bezpiecznie używać? Mam na myśli, czy realistycznie wybrałbym tę implementację, jeśli mój kod będzie działał w WebKit?
IgorGanapolsky
10
@Python: Nie, WebKit nie obsługuje let.
kennytm
Wydaje mi się, że jedynym prawidłowym zastosowaniem tego byłoby, gdybyś wiedział, że wszyscy klienci używają przeglądarki Mozilla, tak jak w przypadku wewnętrznego systemu firmy.
GazB
Lub jeśli programujesz przy użyciu frameworka XUL, frameworku interfejsu Mozilli, w którym budujesz za pomocą css, xml i javascript.
Gerard ONeill
1
@GazB nawet to okropny pomysł! Więc dzisiaj wiesz, że Twoi klienci używają Mozilli, a następnie pojawia się nowa notatka stwierdzająca, że ​​teraz używają czegoś innego. IE powodem, dla którego nasz system płatności jest do bani ... Musisz używać IE8, a nigdy IE9, IE10, Firefox ani Chrome, ponieważ to nie zadziała ...
buzzsawddog
25

Pomysł określania zakresu w JavaScript, gdy został pierwotnie zaprojektowany przez Brendana Eicha, pochodzi z języka skryptowego HyperCard HyperTalk .

W tym języku wyświetlacze zostały wykonane podobnie do stosu kart indeksowych. Była karta główna zwana tłem. Był przezroczysty i może być postrzegany jako dolna karta. Wszelkie treści na tej karcie podstawowej zostały udostępnione kartom umieszczonym na niej. Każda karta umieszczona na górze miała własną treść, która miała pierwszeństwo przed poprzednią kartą, ale nadal miała dostęp do poprzednich kart, jeśli było to pożądane.

Dokładnie tak zaprojektowano system określania zakresu JavaScript. Ma tylko inne nazwy. Karty w JavaScript są znane jako Kontekst Wykonania ECMA . Każdy z tych kontekstów zawiera trzy główne części. Zmienne środowisko, środowisko leksykalne i to powiązanie. Wracając do opisu kart, środowisko leksykalne zawiera całą zawartość poprzednich kart znajdujących się niżej na stosie. Bieżący kontekst znajduje się na górze stosu, a każda zadeklarowana tam zawartość będzie przechowywana w środowisku zmiennych. Zmienne środowisko będzie miało pierwszeństwo w przypadku kolizji nazw.

Powiązanie będzie wskazywać na obiekt zawierający. Czasami zakresy lub konteksty wykonania zmieniają się bez zmiany obiektu zawierającego, na przykład w zadeklarowanej funkcji, w której może być obiekt zawierający windowlub funkcja konstruktora.

Te konteksty wykonania są tworzone za każdym razem, gdy przekazywana jest kontrola. Kontrola jest przekazywana, gdy kod zaczyna się uruchamiać, i odbywa się to przede wszystkim z wykonania funkcji.

To jest wyjaśnienie techniczne. W praktyce należy pamiętać o tym w JavaScript

  • Zakresy są technicznie „kontekstami wykonania”
  • Konteksty tworzą stos środowisk, w których przechowywane są zmienne
  • Górna część stosu ma pierwszeństwo (dolna część to kontekst globalny)
  • Każda funkcja tworzy kontekst wykonania (ale nie zawsze nowe to powiązanie)

Stosując to do jednego z poprzednich przykładów (5. „Zamknięcie”) na tej stronie, można śledzić stos kontekstów wykonania. W tym przykładzie stos zawiera trzy konteksty. Są one zdefiniowane przez kontekst zewnętrzny, kontekst w wywołanej natychmiast funkcji wywoływanej przez var six, a kontekst w funkcji zwróconej wewnątrz natychmiast wywołanej funkcji var six.

i ) Kontekst zewnętrzny. Ma zmienne środowisko a = 1
ii ) Kontekst IIFE, ma środowisko leksykalne a = 1, ale zmienne środowisko a = 6, które ma pierwszeństwo w stosie
iii ) Zwrócony kontekst funkcji, ma leksykalny środowisko a = 6 i jest to wartość, do której odwołuje się alert po wywołaniu.

wprowadź opis zdjęcia tutaj

Travis J
źródło
17

1) Istnieje zasięg globalny, zakres funkcji oraz zakresy zi i catch. Ogólnie nie ma zasięgu na poziomie „bloku” dla zmiennych - instrukcje with i catch dodają nazwy do swoich bloków.

2) Zakresy są zagnieżdżone przez funkcje aż do zasięgu globalnego.

3) Właściwości rozwiązuje się, przechodząc przez łańcuch prototypów. Instrukcja with przenosi nazwy właściwości obiektu do zakresu leksykalnego zdefiniowanego przez blok with.

EDYCJA: ECMAAScript 6 (Harmony) jest przeznaczony do obsługi let, i wiem, że chrome pozwala na flagę „harmonia”, więc może to obsługuje.

Niech będzie wsparcie dla zakresu bloków na poziomie, ale musisz użyć słowa kluczowego, aby tak się stało.

EDYCJA: W oparciu o wskazanie przez Benjamina stwierdzeń „i” w komentarzach, zredagowałem post i dodałem więcej. Zarówno instrukcje with, jak i catch wprowadzają zmienne do odpowiednich bloków, i jest to zakres blokowy. Zmienne te są aliasowane do właściwości przekazywanych do nich obiektów.

 //chrome (v8)

 var a = { 'test1':'test1val' }
 test1   // error not defined
 with (a) { var test1 = 'replaced' }
 test1   // undefined
 a       // a.test1 = 'replaced'

EDYCJA: Przykład wyjaśniający:

test1 ma zakres do bloku z, ale jest aliasowany do a.test1. „Var test1” tworzy nową zmienną test1 w górnym kontekście leksykalnym (funkcyjnym lub globalnym), chyba że jest to własność - którą jest.

Yikes! Zachowaj ostrożność, używając „with” - podobnie jak var jest noop, jeśli zmienna jest już zdefiniowana w funkcji, jest również noop w odniesieniu do nazw importowanych z obiektu! Odwrócenie uwagi od już zdefiniowanej nazwy uczyniłoby to znacznie bezpieczniejszym. Z tego powodu nigdy osobiście nie będę używać.

Gerard ONeill
źródło
Masz tutaj kilka błędów, ponieważ jeden JavaScript ma formy określania zakresu bloków.
Benjamin Gruenbaum
Moje uszy (oczy) są otwarte, Benjamin - Moje powyższe stwierdzenia dotyczą sposobu, w jaki traktuję zakres JavaScript, ale nie opierają się na przeczytaniu specyfikacji. I mam nadzieję, że nie odwołujesz się do instrukcji with (która jest formą określania zakresu obiektów) ani specjalnej składni Mozilla „let”.
Gerard ONeill,
Cóż, withinstrukcja jest formą określania zakresu bloków, ale catchklauzule są znacznie bardziej powszechną formą (zabawne, v8 implementuje się za catchpomocą a with) - to właściwie jedyne formy określania zakresu bloków w samym JavaScript (tzn. Funkcja globalna, spróbuj / złap , zi i ich pochodne), jednak środowiska hosta mają różne pojęcia określania zakresu - na przykład zdarzenia wbudowane w przeglądarce i moduł vm NodeJS.
Benjamin Gruenbaum
Benjamin - z tego co widzę, zarówno za pomocą i catch tylko wprowadza obiekt do bieżącego zakresu (a tym samym właściwości), ale potem po zakończeniu odpowiedniego bloku zmienne są resetowane. Ale na przykład nowa zmienna wprowadzona do catch będzie miała zasięg obejmującej funkcję / metodę.
Gerard ONeill
2
Co dokładnie oznacza zakres blokowy :)
Benjamin Gruenbaum,
9

Odkryłem, że wiele osób, które nie znają JavaScript, mają problem ze zrozumieniem, że dziedziczenie jest domyślnie dostępne w tym języku i że zasięg funkcji jest jak dotąd jedynym zasięgiem. Dostarczyłem rozszerzenie do upiększacza, które napisałem pod koniec ubiegłego roku, o nazwie JSPretty. Zakres funkcji kolorów funkcji w kodzie i zawsze wiąże kolor ze wszystkimi zmiennymi zadeklarowanymi w tym zakresie. Zamknięcie jest zademonstrowane wizualnie, gdy zmienna o kolorze z jednego zakresu jest używana w innym zakresie.

Wypróbuj funkcję w:

Zobacz demo na:

Zobacz kod na:

Obecnie funkcja oferuje obsługę głębokości 16 zagnieżdżonych funkcji, ale obecnie nie koloruje zmiennych globalnych.

austincheney
źródło
1
Nie działa dla mnie w przeglądarce Firefox 26. Wklejam kod lub ładuję plik, klikam przycisk Uruchom i nic się nie dzieje.
mplwork
Zakres i dziedziczenie to dwie różne rzeczy.
Ben Aston
9

JavaScript ma tylko dwa typy zakresu:

  1. Globalny zakres : Globalny jest niczym innym jak zakresem poziomu okna. Tutaj zmienna występuje w całej aplikacji.
  2. Zakres funkcjonalny : Zmienna zadeklarowana w funkcji ze varsłowem kluczowym ma zakres funkcjonalny.

Za każdym razem, gdy wywoływana jest funkcja, tworzony jest obiekt o zmiennym zakresie (i zawarty w łańcuchu zasięgu), po którym następują zmienne w JavaScript.

        a = "global";
         function outer(){ 
              b = "local";
              console.log(a+b); //"globallocal"
         }
outer();

Łańcuch zakresu ->

  1. Poziom okna - ai outerfunkcja znajdują się na najwyższym poziomie w łańcuchu zakresu.
  2. gdy funkcja zewnętrzna wywołała nową variable scope object(i zawartą w łańcuchu zasięgu) dodaną ze zmienną bwewnątrz niej.

Teraz, gdy zmienna jest awymagana, najpierw szuka najbliższego zakresu zmiennej, a jeśli nie ma zmiennej, to przesuwa się do następnego obiektu łańcucha zakresu zmiennej. W tym przypadku jest to poziom okna.

Anszul
źródło
1
Nie jestem pewien, dlaczego nie jest to akceptowana odpowiedź. W rzeczywistości istnieje tylko zakres funkcjonalny (przed ECMA6 nie było „zakresu lokalnego”) i globalne powiązania
texasbruce
9

Aby dodać do innych odpowiedzi, zakres jest listą przeglądową wszystkich zadeklarowanych identyfikatorów (zmiennych) i wymusza ścisły zestaw reguł określających, w jaki sposób są one dostępne dla aktualnie wykonywanego kodu. To wyszukiwanie może mieć na celu przypisanie do zmiennej, która jest referencją LHS (po lewej stronie), lub może być w celu odzyskania jej wartości, która jest referencją RHS (po prawej stronie). Te wyszukiwania są tym, co robi silnik JavaScript wewnętrznie podczas kompilacji i wykonywania kodu.

Z tego punktu widzenia myślę, że zdjęcie pomogłoby mi w ebooku Scopes and Closures autorstwa Kyle'a Simpsona:

wizerunek

Cytując ze swojego ebooka:

Budynek reprezentuje zestaw reguł zagnieżdżonych w naszym programie. Pierwsze piętro budynku reprezentuje aktualnie wykonywany zakres, gdziekolwiek jesteś. Najwyższym poziomem budynku jest zasięg globalny. Rozwiązuje się odniesienia do LHS i RHS, patrząc na bieżące piętro, a jeśli go nie znajdziesz, zabierasz windę na kolejne piętro, tam patrzysz, potem na kolejne itd. Po wejściu na najwyższe piętro (zakres globalny) albo znajdziesz to, czego szukasz, albo nie. Ale musisz przestać niezależnie.

Warto wspomnieć o jednej rzeczy: „Wyszukiwanie zakresu zatrzymuje się po znalezieniu pierwszego dopasowania”.

Ta idea „poziomów zakresu” wyjaśnia, dlaczego „to” można zmienić za pomocą nowo utworzonego zakresu, jeśli jest on sprawdzany w zagnieżdżonej funkcji. Oto link, który zawiera wszystkie te szczegóły. Wszystko , co chciałeś wiedzieć o zakresie javascript

James Drinkard
źródło
8

uruchom kod. mam nadzieję, że da to pomysł na określenie zakresu

Name = 'global data';
document.Name = 'current document data';
(function(window,document){
var Name = 'local data';
var myObj = {
    Name: 'object data',
    f: function(){
        alert(this.Name);
    }
};

myObj.newFun = function(){
    alert(this.Name);
}

function testFun(){
    alert("Window Scope : " + window.Name + 
          "\nLocal Scope : " + Name + 
          "\nObject Scope : " + this.Name + 
          "\nCurrent document Scope : " + document.Name
         );
}


testFun.call(myObj);
})(window,document);
Yeasin Abedin Siam
źródło
8

Zakres globalny :

Zmienne globalne są dokładnie takie jak gwiazdy globalne (Jackie Chan, Nelson Mandela). Możesz uzyskać do nich dostęp (uzyskać lub ustawić wartość) z dowolnej części aplikacji. Funkcje globalne są jak wydarzenia globalne (Nowy Rok, Boże Narodzenie). Możesz je wykonać (zadzwonić) z dowolnej części aplikacji.

//global variable
var a = 2;

//global function
function b(){
   console.log(a);  //access global variable
}

Zakres lokalny:

Jeśli jesteś w USA, możesz znać Kim Kardashian, niesławną celebrytkę (jakoś udaje jej się robić tabloidy). Ale ludzie spoza USA jej nie rozpoznają. Jest lokalną gwiazdą związaną z jej terytorium.

Zmienne lokalne są jak gwiazdy lokalne. Możesz uzyskać do nich dostęp tylko (uzyskać lub ustawić wartość) wewnątrz zakresu. Funkcja lokalna jest jak lokalne zdarzenia - możesz wykonywać (świętować) tylko w tym zakresie. Jeśli chcesz uzyskać do nich dostęp spoza zakresu, pojawi się błąd odniesienia

function b(){
   var d = 21; //local variable
   console.log(d);

   function dog(){  console.log(a); }
     dog(); //execute local function
}

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

Przeczytaj ten artykuł, aby uzyskać dogłębne zrozumienie zakresu

Jhankar Mahbub
źródło
6

Istnieją prawie dwa rodzaje zasięgów JavaScript:

  • zakres każdej deklaracji var jest powiązany z najbardziej natychmiastową funkcją
  • jeśli nie ma funkcji zamykającej dla deklaracji var, jest to zakres globalny

Zatem bloki inne niż funkcje nie tworzą nowego zakresu. To wyjaśnia, dlaczego pętle for zastępują zmienne o zasięgu zewnętrznym:

var i = 10, v = 10;
for (var i = 0; i < 5; i++) { var v = 5; }
console.log(i, v);
// output 5 5

Zamiast tego użyj funkcji:

var i = 10, v = 10;
$.each([0, 1, 2, 3, 4], function(i) { var v = 5; });
console.log(i,v);
// output 10 10

W pierwszym przykładzie nie było zasięgu bloku, więc wstępnie zadeklarowane zmienne zostały zastąpione. W drugim przykładzie pojawił się nowy zakres ze względu na funkcję, więc pierwotnie zadeklarowane zmienne były Zacieniowane, a nie nadpisane.

To prawie wszystko, co musisz wiedzieć w zakresie określania zakresu JavaScript, z wyjątkiem:

Dzięki temu zakres JavaScript jest naprawdę bardzo prosty, choć nie zawsze intuicyjny. Kilka rzeczy, o których należy pamiętać:

  • Deklaracje var są umieszczane na górze zakresu. Oznacza to, że niezależnie od tego, gdzie dzieje się deklaracja var, kompilator wygląda tak, jakby sam var występował na górze
  • łączonych jest wiele deklaracji var w tym samym zakresie

Więc ten kod:

var i = 1;
function abc() {
  i = 2;
  var i = 3;
}
console.log(i);     // outputs 1

jest równa:

var i = 1;
function abc() {
  var i;     // var declaration moved to the top of the scope
  i = 2;
  i = 3;     // the assignment stays where it is
}
console.log(i);

Może się to wydawać sprzeczne z intuicją, ale ma sens z punktu widzenia projektanta języka imperatywnego.

jackbean818
źródło
5

Modern Js, ES6 + const”i„ let

Powinieneś używać zakresu bloków dla każdej tworzonej zmiennej, tak jak w większości innych głównych języków. varjest przestarzały . Dzięki temu kod jest bezpieczniejszy i łatwiejszy w utrzymaniu.

constnależy stosować w 95% przypadków . To sprawia, że odwołanie do zmiennej nie może się zmienić. Właściwości macierzy, obiektów i węzłów DOM mogą ulec zmianie i prawdopodobnie powinny być const.

letnależy stosować dla każdej zmiennej, która ma zostać przypisana ponownie. Obejmuje to wewnątrz pętli for. Jeśli kiedykolwiek zmienisz wartość poza inicjalizację, użyj let.

Blok zakresu oznacza, że ​​zmienna będzie dostępna tylko w nawiasach, w których jest zadeklarowana. Obejmuje to zakresy wewnętrzne, w tym anonimowe funkcje utworzone w twoim zakresie.

Gibolt
źródło
3

Spróbuj tego ciekawego przykładu. W poniższym przykładzie, jeśli a byłaby liczbą zainicjowaną na 0, zobaczyłbyś 0, a następnie 1. Z wyjątkiem tego, że jest obiektem, a javascript przekaże f1 wskaźnik raczej niż jego kopię. W rezultacie otrzymujesz ten sam alert za każdym razem.

var a = new Date();
function f1(b)
{
    b.setDate(b.getDate()+1);
    alert(b.getDate());
}
f1(a);
alert(a.getDate());
Mig82
źródło
3

Istnieją tylko zakresy funkcji w JS. Nie blokuj zakresów! Możesz zobaczyć, co się podnosi.

var global_variable = "global_variable";
var hoisting_variable = "global_hoist";

// Global variables printed
console.log("global_scope: - global_variable: " + global_variable);
console.log("global_scope: - hoisting_variable: " + hoisting_variable);

if (true) {
    // The variable block will be global, on true condition.
    var block = "block";
}
console.log("global_scope: - block: " + block);

function local_function() {
    var local_variable = "local_variable";
    console.log("local_scope: - local_variable: " + local_variable);
    console.log("local_scope: - global_variable: " + global_variable);
    console.log("local_scope: - block: " + block);
    // The hoisting_variable is undefined at the moment.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);

    var hoisting_variable = "local_hoist";
    // The hoisting_variable is now set as a local one.
    console.log("local_scope: - hoisting_variable: " + hoisting_variable);
}

local_function();

// No variable in a separate function is visible into the global scope.
console.log("global_scope: - local_variable: " + local_variable);
koredalin
źródło
(długi czas od opublikowania odpowiedzi) Zablokuj zakres; developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Bob
2

Rozumiem, że istnieją 3 zakresy: zasięg globalny, dostępny globalnie; zasięg lokalny, dostępny dla całej funkcji niezależnie od bloków; oraz zakres bloku, dostępny tylko dla bloku, instrukcji lub wyrażenia, na którym został użyty. Globalny i lokalny zasięg są oznaczone słowem kluczowym „var”, w obrębie funkcji lub poza nią, a zasięg bloku jest wskazany słowem kluczowym „let”.

Dla tych, którzy uważają, że istnieje tylko zasięg globalny i lokalny, wyjaśnij, dlaczego Mozilla miałaby całą stronę opisującą niuanse zakresu bloków w JS.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let

mrmaclean89
źródło
2

Bardzo częstym nieopisanym jeszcze problemem, na który często napotykają kodery front-end, jest zakres widoczny dla wbudowanej procedury obsługi zdarzeń w kodzie HTML - na przykład z

<button onclick="foo()"></button>

Zakres zmiennych, do których on*może odwoływać się atrybut, musi wynosić:

  • global (działające moduły obsługi prawie zawsze odwołują się do zmiennych globalnych)
  • właściwość dokumentu (np. querySelectorjako niezależna zmienna będzie wskazywać document.querySelector; rzadko)
  • właściwość elementu, do którego dołączony jest moduł obsługi (jak wyżej; rzadko)

W przeciwnym razie otrzymasz błąd ReferenceError, gdy zostanie wywołany moduł obsługi. Na przykład, jeśli program obsługi wbudowanej odwołuje się do funkcji zdefiniowanej wewnątrz window.onload lub $(function() {referencja zakończy się niepowodzeniem, ponieważ procedura obsługi wbudowanej może odwoływać się tylko do zmiennych w zakresie globalnym, a funkcja nie jest globalna:

Właściwości documenti właściwości elementu, do którego dołączony jest moduł obsługi, mogą być również określane jako niezależne zmienne wewnątrz modułów obsługi inline, ponieważ moduły obsługi inline są wywoływane w dwóch withblokach , jeden dla elementu document, jeden dla elementu. Łańcuch zasięgu zmiennych wewnątrz tych procedur obsługi to wyjątkowo nieintuicyjny , a działający moduł obsługi zdarzeń prawdopodobnie będzie wymagał globalnej funkcji (i prawdopodobnie należy unikać niepotrzebnego globalnego zanieczyszczenia ).

Ponieważ łańcuch zasięgu wewnątrz procedur obsługi inline jest tak dziwny , a ponieważ procedury obsługi inline wymagają globalnego zanieczyszczenia do działania, a ponieważ procedury obsługi inline czasem wymagają brzydkiego ucieczki łańcucha podczas przekazywania argumentów, prawdopodobnie łatwiej jest ich uniknąć. Zamiast tego dołącz moduły obsługi zdarzeń za pomocą Javascript (podobnie jak w przypadku addEventListener), zamiast znaczników HTML.


Z <script>drugiej strony , w przeciwieństwie do normalnych tagów, które działają na najwyższym poziomie, kod wewnątrz modułów ES6 działa we własnym zakresie prywatnym. Zmienna zdefiniowana na górze normalnego <script>znacznika jest globalna, więc możesz odwoływać się do niej w innych <script>znacznikach, takich jak to:

Ale najwyższy poziom modułu ES6 nie jest globalny. Zmienna zadeklarowana w górnej części modułu ES6 będzie widoczna tylko w tym module, chyba że zmienna jest jawnaexport edytowana lub nie jest przypisana do właściwości obiektu globalnego.

Najwyższy poziom modułu ES6 jest normalnie podobny do wnętrza IIFE na najwyższym poziomie <script>. Moduł może odwoływać się do dowolnych zmiennych, które są globalne, i nic nie może odwoływać się do niczego wewnątrz modułu, chyba że moduł jest do tego specjalnie przeznaczony.

CertainPerformance
źródło
1

W JavaScript są dwa rodzaje zakresu:

  • Zakres lokalny
  • Zakres globalny

Funkcja Below ma lokalną zmienną zakresu carName. I ta zmienna nie jest dostępna spoza funkcji.

function myFunction() {
    var carName = "Volvo";
    alert(carName);
    // code here can use carName
}

Klasa niższa ma zmienną zasięgu globalnego carName. Ta zmienna jest dostępna z każdego miejsca w klasie.

class {

    var carName = " Volvo";

    // code here can use carName

    function myFunction() {
        alert(carName);
        // code here can use carName 
    }
}
Abdur Rahman
źródło
1

ES5 i wcześniej:

Zmienne w JavaScript były początkowo (przed ES6) leksykalnie zakresem funkcji. Termin leksykalnie oznacza, że ​​zakres zmiennych można zobaczyć, „patrząc” na kod.

Każda zmienna zadeklarowana za pomocą varsłowa kluczowego ma zasięg do funkcji. Jeśli jednak inne funkcje zostaną zadeklarowane w ramach tej funkcji, funkcje te będą miały dostęp do zmiennych funkcji zewnętrznych. Nazywa się to łańcuchem zakresu . Działa w następujący sposób:

  1. Kiedy funkcja szuka wartości zmiennej, najpierw patrzy na swój zasięg. Jest to ciało funkcji, tzn. Wszystko między nawiasami klamrowymi {} (z wyjątkiem zmiennych wewnątrz innych funkcji, które są w tym zakresie).
  2. Jeśli nie może znaleźć zmiennej w ciele funkcji, wspina się do łańcucha i patrzy na zakres zmiennej w funkcji, w której funkcja została zdefiniowana . To właśnie oznacza zakres leksykalny, możemy zobaczyć w kodzie, w którym ta funkcja została zdefiniowana, a zatem możemy określić łańcuch zasięgu, patrząc tylko na kod.

Przykład:

// global scope
var foo = 'global';
var bar = 'global';
var foobar = 'global';

function outerFunc () {
 // outerFunc scope
 var foo = 'outerFunc';
 var foobar = 'outerFunc';
 innerFunc();
 
 function innerFunc(){
 // innerFunc scope
  var foo = 'innerFunc';
  console.log(foo);
  console.log(bar);
  console.log(foobar);
  }
}

outerFunc();

Co się dzieje, gdy staramy się zalogować zmiennych foo, bari foobardo konsoli jest następująca:

  1. Próbujemy zalogować foo do konsoli, foo można znaleźć w innerFuncsamej funkcji . Dlatego wartość foo jest rozdzielana na ciąg innerFunc.
  2. Próbujemy zalogować pasek do konsoli, paska nie można znaleźć wewnątrz innerFuncsamej funkcji . Dlatego musimy wspiąć się na łańcuch zasięgu . Najpierw przyglądamy się funkcji zewnętrznej, w której funkcja innerFunczostała zdefiniowana. To jest funkcja outerFunc. W zakresie outerFuncmożemy znaleźć pasek zmiennej, który zawiera ciąg „outerFunc”.
  3. foobar nie można znaleźć w innerFunc. . Dlatego musimy wspiąć się na łańcuch zasięgu do zakresu wewnętrznegoFunc. Nie można go również znaleźć tutaj, wspinamy się o kolejny poziom do zasięgu globalnego (tj. Zasięg najbardziej zewnętrzny). Znajdziemy tutaj zmienną foobar, która zawiera ciąg „global”. Gdyby nie znalazł zmiennej po przejściu łańcucha zasięgu, silnik JS wygenerowałby błąd referencyjny .

ES6 (ES 2015) i starsze:

Wciąż obowiązują te same koncepcje zakresu leksykalnego i zakresu ES6. Wprowadzono jednak nowe sposoby deklarowania zmiennych. Istnieją następujące:

  • let: tworzy zmienną o zasięgu blokowym
  • const: tworzy zmienną o zasięgu blokowym, którą należy zainicjować i której nie można ponownie przypisać

Największą różnicą pomiędzy vari let/ constjest to, że varzakres obejmuje funkcje, podczas gdy let/ constmają zakres blokowy. Oto przykład ilustrujący to:

let letVar = 'global';
var varVar = 'global';

function foo () {
  
  if (true) {
    // this variable declared with let is scoped to the if block, block scoped
    let letVar = 5;
    // this variable declared with let is scoped to the function block, function scoped
    var varVar = 10;
  }
  
  console.log(letVar);
  console.log(varVar);
}


foo();

W powyższym przykładzie letVar rejestruje wartość globalną, ponieważ zmienne zadeklarowane za pomocą letmają zasięg blokowy. Przestają istnieć poza odpowiednim blokiem, więc nie można uzyskać dostępu do zmiennej poza blokiem if.

Willem van der Veen
źródło
0

W EcmaScript5 są głównie dwa zakresy, zakres lokalny i zakres globalny, ale w EcmaScript6 mamy głównie trzy zakresy, zasięg lokalny, zasięg globalny i nowy zakres zwany zakresem blokowym .

Przykładem zakresu bloku jest: -

for ( let i = 0; i < 10; i++)
{
 statement1...
statement2...// inside this scope we can access the value of i, if we want to access the value of i outside for loop it will give undefined.
}
Vivek Mehta
źródło
0

ECMAScript 6 wprowadził słowa kluczowe let i const. Te słowa kluczowe mogą być używane zamiast słowa kluczowego var. W przeciwieństwie do słowa kluczowego var, słowa kluczowe let i const obsługują deklarację zakresu lokalnego w instrukcjach blokowych.

var x = 10
let y = 10
const z = 10
{
  x = 20
  let y = 20
  const z = 20
  {
    x = 30
    // x is in the global scope because of the 'var' keyword
    let y = 30
    // y is in the local scope because of the 'let' keyword
    const z = 30
    // z is in the local scope because of the 'const' keyword
    console.log(x) // 30
    console.log(y) // 30
    console.log(z) // 30
  }
  console.log(x) // 30
  console.log(y) // 20
  console.log(z) // 20
}

console.log(x) // 30
console.log(y) // 10
console.log(z) // 10
Davaakhuu Erdenekhuu
źródło
0

Naprawdę podoba mi się zaakceptowana odpowiedź, ale chcę dodać:

Zakres zbiera i utrzymuje listę przeglądową wszystkich zadeklarowanych identyfikatorów (zmiennych) i egzekwuje ścisły zestaw reguł określających, w jaki sposób są one dostępne dla aktualnie wykonywanego kodu.

Zakres to zestaw reguł wyszukiwania zmiennych według nazwy identyfikatora.

  • Jeśli zmiennej nie można znaleźć w bezpośrednim zasięgu, Engine sprawdza następny zewnętrzny zakres zawierający, kontynuując aż do znalezienia lub do osiągnięcia najbardziej zewnętrznego (czyli globalnego) zakresu.
  • Jest zbiorem reguł, które określają, gdzie i jak można wyszukać zmienną (identyfikator). To wyszukiwanie może być w celu przypisania do zmiennej, która jest referencją LHS (po lewej stronie), lub może być w celu odzyskania jej wartości, która jest referencją RHS (po prawej stronie) .
  • Referencje LHS wynikają z operacji przypisania. Przypisania związane z zakresem mogą występować za pomocą operatora = lub przekazując argumenty do parametrów funkcji (przypisuj do).
  • Mechanizm JavaScript najpierw kompiluje kod przed jego uruchomieniem, a tym samym dzieli instrukcje takie jak var a = 2; na dwa oddzielne kroki: 1.. Po pierwsze, var a, aby zadeklarować to w tym zakresie. Odbywa się to na początku, przed wykonaniem kodu. 2. miejsce Później a = 2, aby wyszukać zmienną (odniesienie LHS) i przypisać do niej, jeśli zostanie znaleziona.
  • Zarówno wyszukiwania referencyjne LHS, jak i RHS rozpoczynają się od aktualnie wykonywanego zakresu, aw razie potrzeby (tzn. Nie znajdują tam tego, czego szukają), idą w górę zagnieżdżonego zakresu, jeden zakres (piętro ) na raz, szukając identyfikatora, dopóki nie dotrą do globalnego (najwyższego piętra) i zatrzymają się, albo go znajdą, albo nie. Niespełnione odwołania RHS powodują zgłoszenie błędu ReferenceError. Niespełnione odwołania LHS powodują automatyczne, niejawnie utworzone globalne o tej nazwie (jeśli nie w trybie ścisłym) lub ReferenceError (jeśli w trybie ścisłym).
  • zakres składa się z szeregu „bąbelków”, z których każdy działa jak pojemnik lub wiadro, w których deklarowane są identyfikatory (zmienne, funkcje). Te bąbelki zagnieżdżają się w sobie, a to zagnieżdżanie jest definiowane w czasie autora.
Ahmed KhaShaba
źródło
-3

W JavaScript są dwa typy zakresów.

  1. Zakres globalny : zmienna, która jest ogłaszana w zakresie globalnym, może być bardzo płynnie używana w dowolnym miejscu programu. Na przykład:

    var carName = " BMW";
    
    // code here can use carName
    
    function myFunction() {
         // code here can use carName 
    }
  2. Zasięg funkcjonalny lub Zasięg lokalny : zmienna zadeklarowana w tym zakresie może być używana tylko we własnej funkcji. Na przykład:

    // code here can not use carName
    function myFunction() {
       var carName = "BMW";
       // code here can use carName
    }
A. Randhawa
źródło