Jaki jest zakres zmiennych w javascript? Czy mają taki sam zakres wewnątrz, jak na zewnątrz funkcji? Czy to w ogóle ma znaczenie? Gdzie są przechowywane zmienne, jeśli są zdefiniowane globalnie?

2128
lYriCAlsSH 1 luty 2009, 11:27

18 odpowiedzi

JavaScript używa łańcuchów zasięgów do ustalenia zakresu dla danej funkcji. Zwykle istnieje jeden zasięg globalny, a każda zdefiniowana funkcja ma swój własny zasięg 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 definiuje zakres.

Element w łańcuchu zasięgu jest w zasadzie Mapą ze wskaźnikiem na jego zasięg nadrzędny.

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

236
krosenvold 14 sierpień 2012, 07:54

Zmienne zadeklarowane globalnie mają globalny zakres. Zmienne zadeklarowane w funkcji są łączone do tej funkcji i cieni zmienne globalne o tej samej nazwie.

(Jestem pewien, że istnieje wiele subtelności, że prawdziwe programy JavaScript będą mogli wskazać w innych odpowiedziach. W szczególności natknąłem się na Ta strona o czym dokładnie this oznacza w dowolnym momencie. Mam nadzieję, że Ten bardziej wprowadzający link wystarczy, abyś zaczął.)

111
gilly3 25 luty 2014, 18:12

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 jak korzystać z nich do wykonania prywatnych członków.

41
geowa4 1 luty 2009, 08:48

Kluczem, jak rozumiem, jest to, że JavaScript ma zakresy na poziomie funkcji w porównaniu z bardziej powszechnym zakresem bloków C.

Oto dobry artykuł na ten temat.

31
James McMahon 15 maj 2012, 17:38

W "JavaScript 1.7" (Rozszerzenie Mozilli do JavaScript) można również zadeklarować zmienne w zakresie bloków z { {X0}} Oświadczenie:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4
26
kennytm 6 kwiecień 2010, 11:19

Idea scoping w JavaScript, gdy pierwotnie zaprojektowany przez Brendan Eich pochodzi z Hypercard język skryptowy hiperalk.

W tym języku wyświetlacze zostały wykonane podobne do stosu kart indeksowych. W tym tle była karta główna. Był przezroczysty i może być postrzegany jako dolna karta. Wszelkie treści na tej karcie bazowej została udostępniona kartami umieszczonymi na niej. Każda karta umieszczona na górze miała własną zawartość, która miała pierwszeństwo przed poprzedniej karty, ale w razie potrzeby miało dostęp do wcześniejszych kart.

Dokładnie zaprojektowano system scopingu JavaScript. Ma tylko inne nazwy. Karty w JavaScript są znane jako Konteksty wykonania ECMA . Każdy z tych kontekstów zawiera trzy główne części. Zmienne środowisko, środowisko leksykalne i to wiązanie. Wracając do odniesienia do kart, środowisko leksykalne zawiera wszystkie zawartość z poprzednich kart niżej w stosie. Obecny kontekst znajduje się na szczycie stosu i dowolna zadeklarowana treść będzie przechowywana w środowisku zmiennym. Zmienne środowisko zajmie pierwszeństwo w przypadku zderzeń nazewnictwa.

Ta wiązanie wskazuje obiekt zawierający. Czasami zakresy zakresów lub kontekstów wykonawczych zmieniają się bez zmiany obiektu zawierającego, takie jak w zadeklarowanej funkcji, w której obiekt zawierający może być window lub funkcja konstruktora.

Te konteksty wykonawcze są tworzone dowolną kontrolę czasu. Sterowanie jest przesyłane, gdy kod rozpoczyna się wykonywania, a to jest przede wszystkim od wykonania funkcji.

To jest wyjaśnienie techniczne. W praktyce ważne jest, aby pamiętać o tym w JavaScript

  • SCOPES są technicznie "konteksty wykonawcze"
  • Konteksty tworzą stos środowisk, w których przechowywane są zmienne
  • Góra stosu ma pierwszeństwo (dno będąc kontekstem globalnym)
  • Każda funkcja tworzy kontekst wykonania (ale nie zawsze nowego tego wiązania)

Stosując to do jednego z poprzednich przykładów (5. "Zamknięcie") na tej stronie, możliwe jest śledzenie stosu kontekstów wykonawczych. W tym przykładzie znajdują się trzy konteksty w stosie. Są one zdefiniowane przez kontekst zewnętrzny, kontekst w natychmiastowym wywołanym funkcie o nazwie Var Six, a kontekst w funkcji zwróconej wewnątrz wywołanej funkcji Var Six jest natychmiast wywołanej funkcji.

i ) kontekst zewnętrzny. Ma zmienne środowisko A = 1
II ) kontekst IIFE, ma środowisko leksykalne A = 1, ale zmienne środowisko A = 6, co ma pierwszeństwo w stosie
III ) Zwrócony kontekst funkcji, ma środowisko leksykalne A = 6 i jest to wartość, do której określono w alercie.

enter image description here

25
Travis J 14 wrzesień 2015, 20:29

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

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

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

EDYCJA: ECMAAScript 6 (Harmony) obsługuje let, i wiem, że chrome pozwala na flagę „harmonii”, więc być może ją obsługuje.

Byłoby wsparciem dla zakresu na poziomie bloku, ale musisz użyć słowa kluczowego, aby to się stało.

EDYCJA: Na podstawie wskazania przez Benjamina w komentarzach with i catch, zredagowałem post i dodałem więcej. Zarówno instrukcje with, jak i catch wprowadzają zmienne do odpowiednich bloków, a to jest zakresem blokowym. Te zmienne są aliasowane do właściwości obiektów do nich przekazanych.

 //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 jest ograniczony do bloku with, ale ma alias do a.test1. „Var test1” tworzy nową zmienną test1 w górnym kontekście leksykalnym (funkcja lub globalny), chyba że jest to właściwość a - którym jest.

Yikes! Uważaj, używając „with” - tak jak var jest noopem, jeśli zmienna jest już zdefiniowana w funkcji, tak samo jest noopem w odniesieniu do nazw importowanych z obiektu! Mała uwaga na już zdefiniowaną nazwę uczyniłaby to znacznie bezpieczniejszym. Osobiście nigdy nie użyję z z tego powodu.

17
Gerard ONeill 6 czerwiec 2014, 17:13

Zauważyłem, że wiele osób, które dopiero zaczynają przygodę z JavaScriptem, ma problem ze zrozumieniem, że dziedziczenie jest domyślnie dostępne w języku, a zakres funkcji jest jak dotąd jedynym zakresem. Dostarczyłem rozszerzenie do kosmetyczki, którą napisałem pod koniec zeszł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 wizualnie pokazane, gdy zmienna o kolorze z jednego zakresu jest używana w innym zakresie.

Wypróbuj tę funkcję pod adresem:

Zobacz demo na:

Zobacz kod pod adresem:

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

9
austincheney 21 marzec 2013, 17:31

Aby dodać do innych odpowiedzi, zakres jest listą wyszukiwania wszystkich zadeklarowanych identyfikatorów (zmiennych) i wymusza ścisły zestaw reguł dotyczących ich dostępności dla aktualnie wykonywanego kodu. To wyszukiwanie może służyć do celów przypisania do zmiennej, która jest odniesieniem LHS (po lewej stronie), lub może służyć do pobierania jej wartości, która jest odniesieniem RHS (po prawej stronie). Te wyszukiwania są tym, co silnik JavaScript robi wewnętrznie podczas kompilowania i wykonywania kodu.

Z tej perspektywy myślę, że pomogłoby zdjęcie, które znalazłem w ebooku „Zakresy i zamknięcia” autorstwa Kyle'a Simpsona:

image

Cytując z jego ebooka:

Budynek reprezentuje zagnieżdżony zestaw reguł naszego programu. Pierwsze piętro budynku reprezentuje Twój aktualnie wykonywany zakres, gdziekolwiek jesteś. Najwyższy poziom budynku to zasięg globalny. Rozwiązujesz odniesienia LHS i RHS, patrząc na swoje obecne piętro, a jeśli go nie znajdziesz, wjeżdżając windą na następne piętro, patrząc tam, potem na następne i tak dalej. Gdy dotrzesz na najwyższe piętro (zakres globalny), albo znajdziesz to, czego szukasz, albo nie. Ale mimo wszystko musisz przestać.

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

Ta idea "poziomu zakresu" wyjaśnia, dlaczego "to" można zmienić za pomocą nowo utworzonego zakresu, jeśli jest podnosić się w zagnieżdżonej funkcji. Oto link, który trafia do wszystkich tych szczegółów, Wszystko, czego chciałeś wiedzieć o zakresie javascript

9
James Drinkard 30 marzec 2016, 13:33

Uruchom kod. mam nadzieję, że da to wyobrażenie o określaniu 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);
8
Yeasin Abedin Siam 18 październik 2014, 09:54

Zakres globalny :

Globalne zmienne są dokładnie jak Global Stars (Jackie Chan, Nelson Mandela). Możesz uzyskać do nich dostęp (uzyskać lub ustawić wartość), z dowolnej części aplikacji. Globalne funkcje są jak globalne wydarzenia (nowy rok, Boże Narodzenie). Możesz wykonać (zadzwonić) je z dowolnej części aplikacji.

//global variable
var a = 2;

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

Zakres lokalny:

Jeśli jesteś w USA, możesz znać Kim Kardashian, niesłyszącą gwiazdę (w jakiś sposób udaje się zrobić tabloidy). Ale ludzie poza USA nie rozpoznają jej. Jest gwiazdą lokalną, związaną z jej terytorium.

Lokalne zmienne są jak lokalne gwiazdy. Możesz je uzyskać tylko do nich (uzyskać lub ustawić wartość) wewnątrz zakresu. Lokalna funkcja jest jak lokalne wydarzenia - możesz wykonać tylko (świętować) w tym zakresie. Jeśli chcesz uzyskać dostęp do nich z zewnątrz zakresu, otrzymasz 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    

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

8
John Slegers 23 luty 2016, 18:56

Istnieją PRAWIE tylko dwa typy zakresów JavaScript:

  • zakres każdej deklaracji var jest powiązany z najbardziej bezpośrednio obejmującą funkcją
  • jeśli nie ma funkcji otaczającej deklarację var, jest to zasięg globalny

Zatem żadne 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żywamy 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 blokowego, więc pierwotnie zadeklarowane zmienne zostały nadpisane. W drugim przykładzie pojawił się nowy zakres ze względu na funkcję, więc początkowo zadeklarowane zmienne były Cieniowane, a nie nadpisywane.

To prawie wszystko, co musisz wiedzieć, jeśli chodzi o zakres JavaScript, z wyjątkiem:

Możesz więc zobaczyć, że zakres JavaScript jest w rzeczywistości niezwykle prosty, choć nie zawsze intuicyjny. Kilka rzeczy, o których należy pamiętać:

  • deklaracje var są przenoszone na początek zakresu. Oznacza to, że bez względu na to, gdzie zdarzy się deklaracja var, dla kompilatora jest to tak, jakby sama zmienna występowała na górze
  • łączy się 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.

6
jackbean818 29 październik 2015, 16:12

Nowoczesny JS, ES6 +, "const 'i' let '

Powinieneś używać skrótu blokowego dla każdej tworzonej zmiennej, podobnie jak większość innych głównych języków. var jest przestarzały . To sprawia, że kod jest bezpieczniejszy i bardziej utrzymujący.

const należy stosować do 95% przypadków . To sprawia, że zmienna referencyjna nie może się zmienić. Właściwości tablicy, obiektu i węchu Dom mogą się zmienić i prawdopodobnie będzie const.

let należy użyć do jakiejkolwiek zmiennej oczekiwania na ponowne przypisanie. Obejmuje to w pętli. Jeśli kiedykolwiek zmienisz wartość poza inicjalizacją, użyj let.

Zakres bloków oznacza, że zmienna będzie dostępna tylko w nawiasach, w których jest zadeklarowany. Rozciąga się to na wewnętrzne zakresy, w tym anonimowe funkcje utworzone w swoim zakresie.

4
Community 20 czerwiec 2020, 09:12

Wypróbuj ten ciekawy przykład. W poniższym przykładzie, jeśli a byłby liczbą zainicjowaną na 0, zobaczysz 0, a następnie 1. Z wyjątkiem tego, że a jest obiektem i javascript przekaże f1 wskaźnik a, a nie 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());
3
Mig82 10 sierpień 2013, 17:37

W JS są tylko zakresy funkcji. 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);
3
koredalin 11 lipiec 2015, 18:13

Rozumiem, że istnieją 3 zakresy: zakres globalny, dostępny globalnie; zasięg lokalny, dostępny dla całej funkcji niezależnie od bloków; i zakres blokowy, dostępne tylko dla bloku, instrukcji lub wyrażenia, w którym zostały użyte. Zasięg globalny i lokalny jest oznaczony słowem kluczowym „var”, w ramach funkcji lub poza nią, a zasięg blokowy jest oznaczony słowem kluczowym „let”.

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

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

2
mrmaclean89 16 wrzesień 2017, 22:35

W EcmaScript5 istnieją głównie dwa zakresy, zakres lokalny i zasięg globalny , ale w EcmaScript6 mamy głównie trzy zakresy, zakres lokalny, zasięg globalny i nowy zakres o nazwie zakres blokowy .

Przykład zakresu blokowego: -

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.
}
0
Mathews Sunny 15 grudzień 2017, 07:49

ECMAScript 6 wprowadził słowa kluczowe let i const. Tych słów kluczowych można używać zamiast słowa kluczowego var. W przeciwieństwie do słowa kluczowego var, słowa kluczowe let i const obsługują deklarację zakresu lokalnego wewnątrz instrukcji 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
0
Davaakhuu Erdenekhuu 9 luty 2018, 15:37