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?
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?
Odpowiedzi:
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) i const
(zakres bloków). Większość innych form deklaracji identyfikatora ma zakres blokowy w trybie ścisłym.
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ć:
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 eval
funkcjach.
Identyfikatory zadeklarowane przy użyciu let
i 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
, const
i 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 let
i const
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.
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 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 mają zasięg do siebie (np. W celu rekurencji).
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.
W eval
ciągach zmiennych zadeklarowane za pomocą var
zostaną umieszczone w bieżącym zakresie lub, jeśli eval
są używane pośrednio, jako właściwości obiektu globalnego.
Poniższa rzuci ReferenceError ponieważ nazwy x
, y
a z
nie 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 y
i z
, ale nie dla x
, ponieważ widoczność x
nie jest ograniczona przez blok. Bloki, które definiują ciała struktur kontrolnych if
, for
i while
zachowują 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 x
widoczny jest poza pętlą, ponieważ var
ma 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ą var
pętli. Jest x
tu zadeklarowana tylko jedna instancja zmiennej , która logicznie leży poza pętlą.
Następujące wydruki 5
, pięć razy, a następnie drukowane 5
po raz szósty na console.log
zewną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 undefined
ponieważ x
jest bloku o zakresie. Oddzwaniania są uruchamiane jeden po drugim asynchronicznie. Nowe zachowanie dla let
zmiennych 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 0
poprzez 4
drukowane .:
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, ReferenceError
ponieważ widoczność x
nie jest ograniczona przez blok; zostanie jednak wydrukowane, undefined
ponieważ zmienna nie została zainicjowana (z powodu if
instrukcji).
if(false) {
var x = 1
}
console.log(x) // here, `x` has been declared, but not initialised
Zmienna zadeklarowana w górnej części for
pętli za pomocą let
jest 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, ReferenceError
ponieważ widoczność x
jest ograniczona przez blok:
if(false) {
let x = 1
}
console.log(typeof x) // undefined, because `x` is block-scoped
Zmienne zadeklarowane za pomocą var
, let
lub const
są 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 var
w kontekście globalnym są dodawane jako właściwości do obiektu globalnego:
var x = 1
console.log(window.hasOwnProperty('x')) // true
let
a const
w 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
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 .
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.
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ąć) .)
Tradycyjnie JavaScript ma tylko dwa typy zakresu:
Nie będę się tym zajmował, ponieważ istnieje już wiele innych odpowiedzi wyjaśniających różnicę.
Te najnowsze specyfikacje JavaScript teraz również umożliwić trzeci zakres:
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";
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 j
jest znana tylko w pierwszej pętli for, ale nie przed i po. Jednak nasza zmienna i
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.
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
:
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.
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 .
Kluczem, jak rozumiem, jest to, że JavaScript ma zakres funkcji na poziomie w porównaniu z bardziej powszechnym zasięgiem bloku C.
W „Javascript 1.7” (rozszerzenie Mozilli do Javascript) można również zadeklarować zmienne o zasięgu blokowym za pomocą let
instrukcji :
var a = 4;
let (a = 3) {
alert(a); // 3
}
alert(a); // 4
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.
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ć.
with
instrukcja jest formą określania zakresu bloków, ale catch
klauzule są znacznie bardziej powszechną formą (zabawne, v8 implementuje się za catch
pomocą 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.
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.
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 = "global";
function outer(){
b = "local";
console.log(a+b); //"globallocal"
}
outer();
Łańcuch zakresu ->
a
i outer
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.
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
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);
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
}
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
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:
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ć:
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.
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ż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 = new Date();
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.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let
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ć:
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:
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óch with
blokach , 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.
W JavaScript są dwa rodzaje zakresu:
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
}
}
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:
// 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
, bar
i foobar
do konsoli jest następująca:
innerFunc
samej funkcji . Dlatego wartość foo jest rozdzielana na ciąg innerFunc
.innerFunc
samej funkcji . Dlatego musimy wspiąć się na łańcuch zasięgu . Najpierw przyglądamy się funkcji zewnętrznej, w której funkcja innerFunc
została zdefiniowana. To jest funkcja outerFunc
. W zakresie outerFunc
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
i let
/ const
jest to, że var
zakres obejmuje funkcje, podczas gdy let
/ const
mają 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ą let
mają 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 = 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
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.
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:
var carName = " BMW";
// code here can use carName
function myFunction() {
// code here can use carName
}
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
}