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?
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:
Globalny - widoczny przez wszystko
Funkcja - widoczna w obrębie funkcji (oraz jej podfunkcji i bloków)
Blok - widoczny w bloku (i jego podblokach)
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:
Niejawnie zdefiniowane właściwości obiektu globalnego (tj. Brak varw trybie nieokreślonym)
import sprawozdania
eval
Niektóre identyfikatory lokalizacji można zadeklarować:
Kontekst globalny
Ciało funkcyjne
Zwykły blok
Góra struktury kontrolnej (np. Pętla, jeśli, podczas itp.)
Korpus struktury kontrolnej
Moduły
Style deklaracji
var
Identyfikatory zadeklarowane za pomocą varmają 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 constmają 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 varwszyscy 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 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 =1let y =1const 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 =1let y =1const 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
... 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
Zmienne zadeklarowane za pomocą var, letlub constsą zawężona do modułów:
// module1.jsvar x =0exportfunction f(){}//module2.jsimport 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
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.
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.
Ł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ąć) .)
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:
Globalny zakres : Zmienne są znane w całej aplikacji, od początku aplikacji (*)
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ę.
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 herefunction 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 herefor(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 herefor(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
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 tę 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.
„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.globalVariablefunction aGlobal( param ){//==window.aGlobal(); //param is only accessible in this functionvar 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 .
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.
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 definedwith(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ć.
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.
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:
Globalny zakres : Globalny jest niczym innym jak zakresem poziomu okna. Tutaj zmienna występuje w całej aplikacji.
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 ->
Poziom okna - ai outerfunkcja znajdują się na najwyższym poziomie w łańcuchu zakresu.
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.
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:
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
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 variablevar a =2;//global functionfunction 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
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:
spróbuj / catch wprowadź nowy zakres TYLKO dla samej zmiennej wyjątku, inne zmienne nie mają nowego zakresu
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.
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.
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 =newDate();function f1(b){
b.setDate(b.getDate()+1);
alert(b.getDate());}
f1(a);
alert(a.getDate());
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);
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.
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
<buttononclick="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ątrzwindow.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.
<scripttype="module">const foo ='foo';</script><script>// Can't access foo here, because the other script is a module
console.log(typeof foo);</script>
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.
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:
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).
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.
Co się dzieje, gdy staramy się zalogować zmiennych foo, bari foobardo konsoli jest następująca:
Próbujemy zalogować foo do konsoli, foo można znaleźć w innerFuncsamej funkcji . Dlatego wartość foo jest rozdzielana na ciąg innerFunc.
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”.
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 scopedlet letVar =5;// this variable declared with let is scoped to the function block, function scopedvar 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.
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.}
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 =10let y =10const z =10{
x =20let y =20const z =20{
x =30// x is in the global scope because of the 'var' keywordlet y =30// y is in the local scope because of the 'let' keywordconst 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
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.
Odpowiedzi:
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:
Poza specjalnymi przypadkami zakresu globalnego i zakresu modułu, zmienne deklarowane są za pomocą
var
(zakres funkcji),let
(zakres bloków) iconst
(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:
Niektóre sposoby deklarowania identyfikatorów:
var
,let
aconst
var
w trybie nieokreślonym)import
sprawozdaniaeval
Niektóre identyfikatory lokalizacji można zadeklarować:
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 weval
funkcjach.let i const
Identyfikatory zadeklarowane przy użyciu
let
iconst
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
,const
ivar
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żywanelet
iconst
nie 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.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
eval
ciągach zmiennych zadeklarowane za pomocąvar
zostaną umieszczone w bieżącym zakresie lub, jeślieval
są używane pośrednio, jako właściwości obiektu globalnego.Przykłady
Poniższa rzuci ReferenceError ponieważ nazwy
x
,y
az
nie mają żadnego znaczenia poza funkcjąf
.Poniższe spowoduje zgłoszenie błędu ReferenceError dla
y
iz
, ale nie dlax
, ponieważ widocznośćx
nie jest ograniczona przez blok. Bloki, które definiują ciała struktur kontrolnychif
,for
iwhile
zachowują się podobnie.Poniżej
x
widoczny jest poza pętlą, ponieważvar
ma zasięg funkcji:... z powodu tego zachowania należy zachować ostrożność przy zamykaniu zmiennych zadeklarowanych za pomocą
var
pętli. Jestx
tu zadeklarowana tylko jedna instancja zmiennej , która logicznie leży poza pętlą.Następujące wydruki
5
, pięć razy, a następnie drukowane5
po raz szósty naconsole.log
zewnątrz pętli:Następujące drukuje
undefined
ponieważx
jest bloku o zakresie. Oddzwaniania są uruchamiane jeden po drugim asynchronicznie. Nowe zachowanie dlalet
zmiennych oznacza, że każda funkcja anonimowa zamknięty przez inną zmienną o nazwiex
(inaczej byłoby to zrobić zvar
), a więc całkowite0
poprzez4
drukowane .:Poniższe NIE rzuci a,
ReferenceError
ponieważ widocznośćx
nie jest ograniczona przez blok; zostanie jednak wydrukowane,undefined
ponieważ zmienna nie została zainicjowana (z powoduif
instrukcji).Zmienna zadeklarowana w górnej części
for
pętli za pomocąlet
jest kierowana do korpusu pętli:Następujące wyrzuci a,
ReferenceError
ponieważ widocznośćx
jest ograniczona przez blok:Zmienne zadeklarowane za pomocą
var
,let
lubconst
są zawężona do modułów:Poniższe elementy zadeklarują właściwość obiektu globalnego, ponieważ zmienne zadeklarowane przy użyciu
var
w kontekście globalnym są dodawane jako właściwości do obiektu globalnego:let
aconst
w kontekście globalnym nie dodawaj właściwości do obiektu globalnego, ale nadal mają zasięg globalny:Parametry funkcji można uznać za zadeklarowane w treści funkcji:
Parametry bloku połowowego mają zasięg do korpusu bloku połowowego:
Nazwane wyrażenia funkcyjne mają zasięg tylko do samego wyrażenia:
W trybie nieokreślonym niejawnie zdefiniowane właściwości obiektu globalnego mają zasięg globalny. W trybie ścisłym pojawia się błąd.
W trybie ścisłym deklaracje funkcji mają zakres funkcji. W trybie ścisłym mają zakres blokowy.
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 .
źródło
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.
źródło
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
this
oznacza w dowolnym momencie. Mam nadzieję, że ten bardziej wprowadzający link wystarczy, aby zacząć) .)źródło
JavaScript w starej szkole
Tradycyjnie JavaScript ma tylko dwa typy zakresu:
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:
Jak utworzyć zmienne zakresu bloków?
Tradycyjnie tworzysz swoje zmienne w następujący sposób:
Zmienne zakresu bloków są tworzone w następujący sposób:
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:
Tutaj możemy zobaczyć, że nasza zmienna
j
jest znana tylko w pierwszej pętli for, ale nie przed i po. Jednak nasza zmiennai
jest 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ć
let
instrukcji.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
let
oś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ć
let
instrukcji. 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
:Jak śledzić obsługę przeglądarki
Aby
let
zapoznać się z aktualnym przeglądem przeglądarek obsługujących to oświadczenie podczas czytania tej odpowiedzi, zobacz tęCan I Use
stronę .(*) 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.
źródło
Oto przykład:
Będziesz chciał zbadać zamknięcia i dowiedzieć się, jak z nich korzystać, aby tworzyć członków prywatnych .
źródło
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.
źródło
W „Javascript 1.7” (rozszerzenie Mozilli do Javascript) można również zadeklarować zmienne o zasięgu blokowym za pomocą
let
instrukcji :źródło
let
.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
window
lub 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
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.
źródło
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.
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ć.
źródło
with
instrukcja jest formą określania zakresu bloków, alecatch
klauzule są znacznie bardziej powszechną formą (zabawne, v8 implementuje się zacatch
pomocą awith
) - 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.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.
źródło
JavaScript ma tylko dwa typy zakresu:
var
sł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ńcuch zakresu ->
a
iouter
funkcja znajdują się na najwyższym poziomie w łańcuchu zakresu.variable scope object
(i zawartą w łańcuchu zasięgu) dodaną ze zmiennąb
wewnątrz niej.Teraz, gdy zmienna jest
a
wymagana, 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.źródło
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:
Cytując ze swojego ebooka:
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
źródło
uruchom kod. mam nadzieję, że da to pomysł na określenie zakresu
źródło
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.
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
Przeczytaj ten artykuł, aby uzyskać dogłębne zrozumienie zakresu
źródło
Istnieją prawie dwa rodzaje zasięgów JavaScript:
Zatem bloki inne niż funkcje nie tworzą nowego zakresu. To wyjaśnia, dlaczego pętle for zastępują zmienne o zasięgu zewnętrznym:
Zamiast tego użyj funkcji:
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ć:
Więc ten kod:
jest równa:
Może się to wydawać sprzeczne z intuicją, ale ma sens z punktu widzenia projektanta języka imperatywnego.
źródło
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.
var
jest przestarzały . Dzięki temu kod jest bezpieczniejszy i łatwiejszy w utrzymaniu.const
należ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
.let
należ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żyjlet
.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.
źródło
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.
źródło
Istnieją tylko zakresy funkcji w JS. Nie blokuj zakresów! Możesz zobaczyć, co się podnosi.
źródło
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
źródło
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
Zakres zmiennych, do których
on*
może odwoływać się atrybut, musi wynosić:querySelector
jako niezależna zmienna będzie wskazywaćdocument.querySelector
; 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:Pokaż fragment kodu
Właściwości
document
i 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óchwith
blokach , jeden dla elementudocument
, 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.Pokaż fragment kodu
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:Pokaż fragment kodu
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 jawna
export
edytowana lub nie jest przypisana do właściwości obiektu globalnego.Pokaż fragment kodu
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.źródło
W JavaScript są dwa rodzaje zakresu:
Funkcja Below ma lokalną zmienną zakresu
carName
. I ta zmienna nie jest dostępna spoza funkcji.Klasa niższa ma zmienną zasięgu globalnego
carName
. Ta zmienna jest dostępna z każdego miejsca w klasie.źródło
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ą
var
sł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:Przykład:
Co się dzieje, gdy staramy się zalogować zmiennych
foo
,bar
ifoobar
do konsoli jest następująca:innerFunc
samej funkcji . Dlatego wartość foo jest rozdzielana na ciąginnerFunc
.innerFunc
samej funkcji . Dlatego musimy wspiąć się na łańcuch zasięgu . Najpierw przyglądamy się funkcji zewnętrznej, w której funkcjainnerFunc
została zdefiniowana. To jest funkcjaouterFunc
. W zakresieouterFunc
możemy znaleźć pasek zmiennej, który zawiera ciąg „outerFunc”.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 blokowymconst
: 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
var
ilet
/const
jest to, żevar
zakres obejmuje funkcje, podczas gdylet
/const
mają zakres blokowy. Oto przykład ilustrujący to:W powyższym przykładzie letVar rejestruje wartość globalną, ponieważ zmienne zadeklarowane za pomocą
let
mają zasięg blokowy. Przestają istnieć poza odpowiednim blokiem, więc nie można uzyskać dostępu do zmiennej poza blokiem if.źródło
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: -
źródło
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.
źródło
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.
źródło
W JavaScript są dwa typy zakresów.
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:
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:
źródło