Vraag Wat is het bereik van variabelen in JavaScript?


Wat is de reikwijdte van variabelen in javascript? Hebben ze dezelfde reikwijdte binnen als tegenover een functie buiten? Of maakt het zelfs uit? Waar worden de variabelen ook opgeslagen als ze globaal worden gedefinieerd?


1711
2018-02-01 08:27


oorsprong


antwoorden:


Ik denk dat het beste wat ik kan doen, is om je een aantal voorbeelden te geven om te studeren. Javascript-programmeurs zijn praktisch gerangschikt op basis van hoe goed ze de scope begrijpen. Het kan soms nogal contra-intuïtief zijn.

  1. Een globaal geschaalde variabele

    // global scope
    var a = 1;
    
    function one() {
      alert(a); // alerts '1'
    }
    
  2. Lokaal bereik

    // global scope
    var a = 1;
    
    function two(a) {
      // local scope
      alert(a); // alerts the given argument, not the global value of '1'
    }
    
    // local scope again
    function three() {
      var a = 3;
      alert(a); // alerts '3'
    }
    
  3. tussen-: Niet zoiets als block scope in JavaScript (ES5; ES6 introduceert let)

    een.

    var a = 1;
    
    function four() {
      if (true) {
        var a = 4;
      }
    
      alert(a); // alerts '4', not the global value of '1'
    }
    

    b.

    var a = 1;
    
    function one() {
      if (true) {
        let a = 4;
      }
    
      alert(a); // alerts '1' because the 'let' keyword uses block scoping
    }
    
  4. tussen-: Objecteigenschappen

    var a = 1;
    
    function Five() {
      this.a = 5;
    }
    
    alert(new Five().a); // alerts '5'
    
  5. gevorderd: Sluiting

    var a = 1;
    
    var six = (function() {
      var a = 6;
    
      return function() {
        // JavaScript "closure" means I have access to 'a' in here,
        // because it is defined in the function in which I was defined.
        alert(a); // alerts '6'
      };
    })();
    
  6. gevorderd: Op prototypen gebaseerde scoopresolutie

    var a = 1;
    
    function seven() {
      this.a = 7;
    }
    
    // [object].prototype.property loses to
    // [object].property in the lookup chain. For example...
    
    // Won't get reached, because 'a' is set in the constructor above.
    seven.prototype.a = -1;
    
    // Will get reached, even though 'b' is NOT set in the constructor.
    seven.prototype.b = 8;
    
    alert(new seven().a); // alerts '7'
    alert(new seven().b); // alerts '8'
    

  7. Global + Local: Een extra complex geval

    var x = 5;
    
    (function () {
        console.log(x);
        var x = 10;
        console.log(x); 
    })();
    

    Dit wordt afgedrukt undefined en 10 liever dan 5 en 10 omdat JavaScript altijd variabele declaraties (geen initialisaties) naar de top van het bereik verplaatst, waardoor de code equivalent is aan:

    var x = 5;
    
    (function () {
        var x;
        console.log(x);
        x = 10;
        console.log(x); 
    })();
    
  8. Vangstsclausule-variabele

    var e = 5;
    console.log(e);
    try {
        throw 6;
    } catch (e) {
        console.log(e);
    }
    console.log(e);
    

    Dit wordt afgedrukt 5, 6, 5. Binnen de catch-clausule e schaduwen globale en lokale variabelen. Maar deze speciale scope is alleen voor de gevangen variabele. Als je schrijft var f; in de catch-clausule, dan is het precies hetzelfde alsof je het vóór of na het try-catch-blok had gedefinieerd.


2262
2018-02-01 08:58



Javascript maakt gebruik van bereikketens om de reikwijdte voor een bepaalde functie vast te stellen. Er is meestal één globaal bereik en elke gedefinieerde functie heeft zijn eigen geneste bereik. Elke functie gedefinieerd binnen een andere functie heeft een lokaal bereik dat gekoppeld is aan de buitenfunctie. Het is altijd de positie in de bron die het bereik definieert.

Een element in de scopeketen is eigenlijk een kaart met een verwijzing naar het bovenliggende bereik.

Bij het oplossen van een variabele begint javascript bij de binnenste scope en zoekt het naar buiten.


219
2018-02-01 08:35



Algemeen verklaarde variabelen hebben een globaal bereik. Variabelen die binnen een functie worden gedeclareerd, worden naar die functie geschaald en overschrijden globale variabelen met dezelfde naam.

(Ik ben er zeker van dat er veel subtiliteiten zijn die echte JavaScript-programmeurs in andere antwoorden kunnen aanwijzen. Ik kwam met name deze pagina over wat precies this betekent op elk moment. Hopelijk deze meer inleidende link is genoeg om je op weg te helpen.)


93
2018-02-01 08:31



Oldschool JavaScript

Traditioneel heeft JavaScript eigenlijk maar twee soorten bereik:

  1. Global Scope : Variabelen zijn bekend in de hele applicatie, vanaf het begin van de applicatie (*)
  2. Functioneel bereik : Variabelen zijn binnen bekend de functie ze worden aangegeven vanaf het begin van de functie (*)

Ik zal hier niet verder op ingaan, omdat er al veel andere antwoorden zijn die het verschil verklaren.


Modern JavaScript

De meest recente JavaScript-specificaties laat nu ook een derde scope toe:

  1. Blokkeer bereik : Variabelen zijn binnen bekend het blokze worden binnen verklaard, vanaf het moment waarop ze worden opgegeven (**)

Hoe maak ik blokbereikvariabelen?

Traditioneel maak je je variabelen als volgt aan:

var myVariable = "Some text";

Blokwerkingsvariabelen worden als volgt gemaakt:

let myVariable = "Some text";

Dus wat is het verschil tussen functionele scope en block scope?

Neem de volgende code door om het verschil tussen het functionele bereik en het blokbereik te begrijpen:

// 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

Hier kunnen we zien dat onze variabele j is alleen bekend in de eerste lus, maar niet voor en na. Maar toch, onze variabele i is bekend in de volledige functie.

Bedenk ook dat variabelen die zijn afgebakend met blokken niet bekend zijn voordat ze worden gedeclareerd omdat ze niet worden gehesen. Het is ook niet toegestaan ​​om dezelfde block scoped variabele binnen hetzelfde blok opnieuw in te stellen. Dit maakt variabelen met blokafhankelijkheid minder foutgevoelig dan globaal of functioneel geschaalde variabelen, die worden gehesen en die geen fouten veroorzaken in het geval van meerdere declaraties.


Is het veilig om blokbereikvariabelen vandaag te gebruiken?

Of het vandaag veilig is om te gebruiken, hangt af van uw omgeving:

  • Als u JavaScript-code aan de serverzijde schrijft (Node.js), kunt u het veilig gebruiken let uitspraak.

  • Als u client-side JavaScript-code schrijft en een transponder gebruikt (zoals traceur), kunt u het veilig gebruiken let verklaring, maar uw code is waarschijnlijk alles behalve optimaal met betrekking tot de prestaties.

  • Als u JavaScript-code op de client schrijft en geen transponder gebruikt, moet u de ondersteuning voor de browser overwegen.

    Vandaag, 23 februari 2016, zijn dit enkele browsers die ofwel niet ondersteunen let of heb slechts gedeeltelijke ondersteuning:

    • Internetverkenner 10 en hieronder (geen ondersteuning)
    • Firefox 43 en hieronder (geen ondersteuning)
    • Safari 9 en hieronder (geen ondersteuning)
    • Opera Mini 8 en hieronder (geen ondersteuning)
    • Android-browser 4 en hieronder (geen ondersteuning)
    • Opera 36 en hieronder (gedeeltelijke ondersteuning)
    • Chome 51 en hieronder (gedeeltelijke ondersteuning)

enter image description here


Hoe browserondersteuning te volgen

Voor een up-to-date overzicht van welke browsers het ondersteunen let verklaring op het moment dat u dit antwoord leest, zie deze Can I Use pagina.


(*) Globaal en functioneel scoped variabelen kunnen worden geïnitialiseerd en gebruikt voordat ze worden gedeclareerd omdat JavaScript-variabelen zijn gehesen. Dit betekent dat aangiften altijd veel tot de top behoren.

(**) Block-scoped-variabelen worden niet gehesen


55
2018-02-23 18:51



Hier is een voorbeeld:

<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>

U wilt sluitingen onderzoeken en hoe u ze kunt gebruiken privé leden.


35
2018-02-01 08:48



De sleutel, zoals ik het begrijp, is dat Javascript functioneringsniveau-scoping heeft versus de meer gebruikelijke C-blockscoping.

Hier is een goed artikel over het onderwerp.


28
2018-05-15 17:38



In "Javascript 1.7" (Mozilla's uitbreiding naar Javascript) kan men ook block-scope-variabelen declareren let uitspraak:

 var a = 4;
 let (a = 3) {
   alert(a); // 3
 }
 alert(a);   // 4

23
2018-04-06 11:19



Het idee van scoping in JavaScript wanneer oorspronkelijk ontworpen door Brendan Eich kwam van de HyperCard scripttaal HyperTalk.

In deze taal waren de schermen vergelijkbaar met een stapel indexkaarten. Er was een masterkaart die de achtergrond wordt genoemd. Het was transparant en kan worden gezien als de onderste kaart. Alle inhoud op deze basiskaart is gedeeld met kaarten die erop zijn geplaatst. Elke bovenop geplaatste kaart had zijn eigen inhoud die voorrang had op de vorige kaart, maar had desgewenst toch toegang tot de eerdere kaarten.

Dit is precies hoe het JavaScript-scoping-systeem is ontworpen. Het heeft gewoon andere namen. De kaarten in JavaScript staan ​​bekend als ExecutiecontextenECMA. Elk van deze contexten bevat drie hoofdonderdelen. Een variabele omgeving, een lexicale omgeving en een deze binding. Terugkomend op de kaartreferentie, bevat de lexicale omgeving alle inhoud van eerdere kaarten die lager in de stapel liggen. De huidige context staat bovenaan de stapel en alle inhoud die daar wordt gedeclareerd, wordt opgeslagen in de variabele omgeving. De variabele omgeving heeft voorrang in het geval van het benoemen van botsingen.

De deze binding verwijst naar het bevattende object. Soms worden scopes of uitvoeringscontexten gewijzigd zonder dat het bijbehorende object wordt gewijzigd, zoals in een gedeclareerde functie waar het bevattende object zich kan bevinden window of een constructorfunctie.

Deze uitvoeringscontexten worden gemaakt op elk moment dat de controle wordt overgedragen. Controle wordt overgedragen wanneer de code begint uit te voeren, en dit wordt voornamelijk gedaan door het uitvoeren van de functie.

Dus dat is de technische uitleg. In de praktijk is het belangrijk om dat in JavaScript te onthouden

  • Scopes zijn technisch gezien "Executiecontexten"
  • Contexten vormen een stapel omgevingen waarin variabelen worden opgeslagen
  • De bovenkant van de stapel heeft voorrang (de bodem is de globale context)
  • Elke functie maakt een uitvoeringscontext (maar niet altijd een nieuwe deze binding)

Als u dit op een van de voorgaande voorbeelden toepast (5. "Afsluiting") op deze pagina, is het mogelijk om de stapel uitvoeringscontexten te volgen. In dit voorbeeld zijn er drie contexten in de stapel. Ze worden gedefinieerd door de buitencontext, de context in de onmiddellijk opgeroepen functie genoemd door var six en de context in de geretourneerde functie binnen de direct aangeroepen functie van var six.

ik) De buitenste context. Het heeft een variabele omgeving van a = 1
ii) De IIFE-context, het heeft een lexicale omgeving van a = 1, maar een variabele omgeving van a = 6 die voorrang heeft in de stapel
iii) De geretourneerde functiecontext, het heeft een lexicale omgeving van a = 6 en dat is de waarde waarnaar wordt verwezen in de waarschuwing wanneer deze wordt aangeroepen.

enter image description here


18
2017-09-14 20:29



1) Er is een globaal bereik, een functiescope en de met en catch-scopes. Er is in het algemeen geen 'blok' niveaubereik voor variabelen - de met en de vangstinstructies voegen namen toe aan hun blokken.

2) Scopes worden genest door functies tot het globale bereik.

3) Eigenschappen worden opgelost door de prototypeketen te doorlopen. Met de instructie met worden namen van objecteigenschappen toegevoegd aan het lexicale bereik dat wordt gedefinieerd door het met-blok.

EDIT: ECMAAScript 6 (Harmony) is gespecificeerd als ondersteuning voor let, en ik weet dat chrome een 'harmony'-vlag toestaat, dus misschien ondersteunt het wel ..

Laten we een ondersteuning zijn voor scoping op blokniveau, maar je moet het sleutelwoord gebruiken om het te laten gebeuren.

EDIT: Gebaseerd op de opmerking van Benjamin over de with- en catch-statements in de reacties, heb ik het bericht bewerkt en meer toegevoegd. Zowel de met-als de catch-instructies introduceren variabelen in hun respectieve blokken, en dat is een block scope. Deze variabelen zijn ge aliased naar de eigenschappen van de objecten die aan hen zijn doorgegeven.

 //chrome (v8)

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

EDIT: verduidelijkend voorbeeld:

test1 is scoped naar het met blok, maar is gealiast naar a.test1. 'Var test1' maakt een nieuwe variabele test1 in de bovenste lexicale context (functie of globaal), tenzij het een eigenschap is van een - wat het is.

Yikes! Wees voorzichtig met 'met' - net zoals var een noop is als de variabele al in de functie is gedefinieerd, is het ook een noop met betrekking tot namen die uit het object zijn geïmporteerd! Een beetje op de hoogte zijn van de naam die al wordt gedefinieerd, zou dit veel veiliger maken. Ik zal dit persoonlijk nooit gebruiken.


16
2017-10-25 00:41



Ik ontdekte dat veel mensen die nieuw zijn bij JavaScript moeite hebben om te begrijpen dat inheritance standaard beschikbaar is in de taal en dat de functieomvang tot nu toe de enige scope is. Ik leverde een uitbreiding op een verfraaiing die ik eind vorig jaar schreef, genaamd JSPretty. Het functiekader Functiekleuren in de code en associeert een kleur altijd met alle variabelen die in dat bereik zijn gedeclareerd. De sluiting wordt visueel gedemonstreerd wanneer een variabele met een kleur uit één scope in een ander bereik wordt gebruikt.

Probeer de functie op:

Bekijk een demo op:

Bekijk de code op:

Momenteel biedt de functie ondersteuning voor een diepte van 16 geneste functies, maar momenteel worden er geen globale variabelen ingekleurd.


9
2018-03-21 17:31