In JavaScript is bereik de context waarin code wordt uitgevoerd. Er zijn drie soorten reikwijdte: globaal bereik, lokaal bereik (soms ook wel "functiemaximum" genoemd) en eval scope.
Code gedefinieerd met var
binnen een functie is lokaal scoped, en is alleen "zichtbaar" voor andere uitdrukkingen in die functie, die code bevat binnen alle geneste / onderliggende functies. Variabelen gedefinieerd in het globale bereik zijn overal toegankelijk, omdat dit het hoogste niveau en de laatste halte in de consolidatieketen is.
Bestudeer de code die volgt en zorg ervoor dat u begrijpt dat elke verklaring van foo
is uniek vanwege de reikwijdte.
Voorbeeld: sample110.html
Zorg dat je dat begrijpt foo
variabele bevat een andere waarde omdat elke waarde wordt gedefinieerd in een specifiek afgebakende scope.
Een onbeperkt aantal functies en eval scopes kunnen worden gemaakt, terwijl slechts één globaal bereik door een JavaScript-omgeving wordt gebruikt.
De wereldwijde scope is de laatste stop in de scope-keten.
Functies die functies bevatten, maken gestapelde uitvoeringsscopes. Deze stapels, die aan elkaar worden geketend, worden vaak de scopeketen genoemd.
Sinds logische verklaringen (als
) en looping statements (voor
) geen bereik maken, variabelen kunnen elkaar overschrijven. Bestudeer de volgende code en zorg ervoor dat u begrijpt dat de waarde van foo
wordt opnieuw gedefinieerd wanneer het programma de code uitvoert.
Voorbeeld: sample111.html
Zo foo
wordt gewijzigd terwijl de code wordt uitgevoerd, omdat JavaScript geen functie met alleen het bereik Blokkering, Globaal of EVal heeft.
var
Binnenkant van functies om variabelen te declareren en scopes te vermijden GotchasJavaScript declareert alle variabelen zonder a var
aangifte (zelfs die in een functie of ingekapselde functies) om in de globale scope te zijn in plaats van de beoogde lokale scope. Bekijk de code die volgt en merk dat zonder het gebruik van var
om de balk te declareren, wordt de variabele feitelijk gedefinieerd in de globale scope en niet de lokale scope, waar deze zou moeten zijn.
Voorbeeld: sample112.html
Het concept om hier weg te nemen is dat je altijd moet gebruiken var
bij het definiëren van variabelen binnen een functie. Dit voorkomt dat u te maken krijgt met potentieel verwarrende scopeproblemen. De uitzondering op deze conventie is natuurlijk wanneer u vanuit een functie eigenschappen in het globale bereik wilt maken of wijzigen.
Er is een opzoekketen die wordt gevolgd wanneer JavaScript zoekt naar de waarde die aan een variabele is gekoppeld. Deze keten is gebaseerd op de hiërarchie van de reikwijdte. In de code die volgt, registreer ik de waarde van sayHiText
van de func2
functiebereik.
Voorbeeld: sample113.html
Hoe is de waarde van sayHiText
gevonden wanneer het niet binnen de reikwijdte van de func2
functie? JavaScript ziet er eerst in de func2
functie voor een variabele met de naam sayHiText
. Niet vinden func2
daar kijkt het naar op func2
s ouderfunctie, func1
. De sayHiText
variabele is niet gevonden in de func1
bereik, dus, dan gaat JavaScript verder tot het wereldwijde bereik waar sayHiText
wordt gevonden, op welk punt de waarde van sayHiText
is geleverd. Als sayHiText
was niet gedefinieerd in de globale reikwijdte, onbepaald
zou zijn geretourneerd door JavaScript.
Dit is een heel belangrijk begrip om te begrijpen. Laten we een ander codevoorbeeld onderzoeken, een waarin we drie waarden uit drie verschillende bereiken pakken.
Voorbeeld: sample114.html
De waarde voor z
is lokaal voor de bar
functie en de context waarin de console.log
wordt aangeroepen. De waarde voor Y
is in de foo
functie, die de ouder is van bar()
, en de waarde voor X
is in de mondiale reikwijdte. Al deze zijn toegankelijk voor de bar
functie via de scopeketen. Zorg ervoor dat u begrijpt dat verwijzingsvariabelen in de bar
De functie controleert de scopeketen helemaal naar de variabelen waarnaar wordt verwezen.
De reikwijdte, als je erover nadenkt, is niet zo verschillend van de prototypeketen. Beide zijn eenvoudigweg een manier om een waarde op te zoeken door een systematische en hiërarchische reeks locaties te controleren.
In het codevoorbeeld dat volgt, wordt een variabele genoemd X
bestaat in hetzelfde bereik als waarin het wordt onderzocht console.log
. Deze "lokale" waarde van X
wordt gebruikt, en je zou kunnen zeggen dat het schaduwen of maskers met dezelfde naam draagt X
variabelen die verder in de consolidatieketen zijn gevonden.
Voorbeeld: sample115.html
Onthoud dat de zoekopdracht naar het bereik eindigt wanneer de variabele wordt gevonden in de dichtstbijzijnde beschikbare link van de keten, zelfs als dezelfde variabelenaam verderop in de keten wordt gebruikt.
Omdat functies het bereik bepalen en functies kunnen worden doorgegeven, net zoals elke JavaScript-waarde, zou men kunnen denken dat het ontcijferen van de scopeketen ingewikkeld is. Het is eigenlijk heel simpel. De scopeketen wordt bepaald op basis van de locatie van een functie tijdens de definitie, niet tijdens de aanroeping. Dit wordt ook lexicale scoping genoemd. Denk hier lang en hard over na, omdat de meeste mensen er vaak in JavaScript-code over struikelen.
De scopeketen wordt gemaakt voordat u een functie aanroept. Hierdoor kunnen we sluitingen maken. We kunnen bijvoorbeeld een functie een geneste functie laten retourneren naar het globale bereik, maar onze functie kan via de bereikketen nog steeds de scope van de bovenliggende functie openen. In het volgende voorbeeld definiëren we een parentFunction
die een anonieme functie retourneert en we roepen de geretourneerde functie van de globale scope. Omdat onze anonieme functie werd gedefinieerd als onderdeel van parentFunction
, het heeft nog steeds toegang tot parentFunctions
bereik wanneer het wordt opgeroepen. Dit wordt een afsluiting genoemd.
Voorbeeld: sample116.html
Het idee dat je hier moet wegnemen is dat de scopeketen tijdens de definitie letterlijk wordt bepaald op de manier waarop de code is geschreven. Als u functies in uw code doorgeeft, verandert dit niet de reikwijdte.
Neem wat u hebt geleerd over de bereikketen en de scope-lookup van dit artikel, en een afsluiting moet niet overdreven ingewikkeld zijn om te begrijpen. In het volgende voorbeeld maken we een functie genaamd countUpFromZero
. Deze functie retourneert in feite een verwijzing naar de onderliggende functie die erin is opgenomen. Wanneer deze onderliggende functie (geneste functie) wordt aangeroepen, heeft deze nog steeds toegang tot het bereik van de bovenliggende functie vanwege de bereikketen.
Voorbeeld: sample117.html
Elke keer dat de countUpFromZero
functie wordt aangeroepen, de anonieme functie vervat in (en teruggestuurd van) de countUpFromZero
functie heeft nog steeds toegang tot het bereik van de ouderfunctie. Deze techniek, gefaciliteerd via de scopeketen, is een voorbeeld van een afsluiting.
Als je denkt dat ik te veel vereenvoudigde sluitingen heb, heb je waarschijnlijk gelijk in deze gedachte. Maar ik deed dit met opzet omdat ik geloof dat de belangrijke delen voortkomen uit een goed begrip van functies en reikwijdte, niet noodzakelijk de complexiteit van de uitvoeringscontext. Als u behoefte heeft aan een grondige duik in sluitingen, bekijk dan JavaScript-sluitingen.