Omfattning och tillslutningar

I JavaScript är räckvidden det sammanhang där kod körs. Det finns tre typer av räckvidd: globalt räckvidd, lokalt räckvidd (ibland benämnt "funktionsomfång") och eval scope.

Kod definierad med var Inne i en funktion är lokalt scoped, och är bara "synlig" för andra uttryck i den funktionen, som inkluderar kod inom alla kapslade / barnfunktioner. Variabler definierade i det globala räckviddet kan nås var som helst eftersom det är den högsta nivån och sista stoppet i omfångskedjan.

Undersök koden som följer och se till att du förstår att varje deklaration av foo är unik på grund av omfattningen.

Prov: prov110.html

 

Se till att du förstår att var och en foo variabeln innehåller ett annat värde eftersom var och en definieras i ett specifikt avgränsat räckvidd.

Ett obegränsat antal funktions- och eval-områden kan skapas, medan endast ett globalt räckvidd används av en JavaScript-miljö.

Det globala räckviddet är det sista stoppet i omfångskedjan.

Funktioner som innehåller funktioner skapar stabila utföringsfält. Dessa staplar, som är kedjda ihop, benämns ofta omfattningskedjan.


JavaScript har inte blockomfattning

Eftersom logiska uttalanden (om) och looping uttalanden (för) skapar inte en räckvidd, variabler kan skriva över varandra. Undersök följande kod och se till att du förstår att värdet av foo omdefinieras när programmet kör koden.

Prov: sample111.html

 

foo ändras när koden körs, eftersom JavaScript inte har någon funktion för funktionalitet, endast globalt eller eval.


Använda sig av var Inne i funktioner för att deklarera variabler och undvika omfattning Gotchas

JavaScript kommer att förklara några variabler som saknar a var deklaration (även de som ingår i en funktion eller inkapslade funktioner) att vara i det globala räckviddet istället för det avsedda lokala räckviddet. Titta på koden som följer och märka det utan att använda var att deklarera streck är variabeln faktiskt definierad i det globala räckviddet och inte den lokala räckvidden, där den borde vara.

Prov: prov112.html

 

Konceptet att ta bort här är att du alltid ska använda var när man definierar variabler inuti en funktion. Detta kommer att förhindra att du hanterar potentiellt förvirrande räckviddsproblem. Undantaget från denna konvention är naturligtvis när du vill skapa eller ändra egenskaper i det globala räckviddet från en funktion.


Scope Chain (aka Lexical Scoping)

Det finns en uppslagskedja som följs när JavaScript letar efter värdet associerat med en variabel. Denna kedja är baserad på omfattningen av hierarkin. I koden som följer loggar jag värdet på sayHiText från func2 funktionens omfattning.

Prov: sample113.html

 

Hur är värdet av sayHiText hittade när det inte finns inne i ramen för func2 fungera? JavaScript ser först ut i func2 funktion för en variabel som heter sayHiText. Inte hitta func2 där ser det upp till func2s föräldrafunktion, FUNC1. De sayHiText variabel finns inte i FUNC1 omfattning, antingen, så JavaScript fortsätter så upp till det globala räckvidd där sayHiText hittas, vid vilken tidpunkt värdet av sayHiText är levererad. Om sayHiText hade inte definierats i det globala räckviddet, odefinierad skulle ha returnerats av JavaScript.

Detta är ett mycket viktigt begrepp att förstå. Låt oss undersöka ett annat kodexempel där vi tar tre värden från tre olika områden.

Prov: sample114.html

 

Värdet för z är lokal till bar funktion och det sammanhang där console.log åberopas. Värdet för y är i foo funktion, som är förälder till bar(), och värdet för x är i global omfattning. Alla dessa är tillgängliga för bar funktion via omfattningskedjan. Se till att du förstår de referensvariablerna i bar funktionen kommer att kontrollera hela vägen för kedjan för variablerna som refereras.

Omfattningskedjan, om du tänker på det, är inte så annorlunda än prototypkedjan. Båda är helt enkelt ett sätt att värdera ett värde genom att kontrollera en systematisk och hierarkisk uppsättning platser.


Scope Chain Lookup returnerar det första grundvärdet

I det kodprov som följer, kallas en variabel x existerar inom samma räckvidd som det undersöks med console.log. Detta "lokala" värde av x används, och man kan säga att det skuggar, eller maskerar, den identiska namnet x variablerna hittades längre fram inom ramen för kedjan.

Prov: sample115.html

 

Kom ihåg att scope lookup slutar när variabeln finns i närmaste tillgängliga länk i kedjan, även om samma variabla namn används vidare upp i kedjan.


Omfattning bestäms under funktionsdefinition, inte invokation

Eftersom funktioner bestämmer räckvidd och funktioner kan överföras precis som vilket som helst JavaScript-värde kan man tro att dechiffreringen av omfattningskedjan är komplicerad. Det är faktiskt väldigt enkelt. Omfattningskedjan bestäms utifrån placeringen av en funktion under definitionen, inte under invokation. Detta kallas också lexical scoping. Tänk lång och svårt om detta, eftersom de flesta ofta snubblar över det i JavaScript-kod.

Omfattningskedjan skapas innan du påbereder en funktion. På grund av detta kan vi skapa nedläggningar. Till exempel kan vi få en funktion att returnera en kapslad funktion till det globala räckviddet, men vår funktion kan fortfarande, via omfattningskedjan, få tillgång till dess överordnade funktion. I följande exempel definierar vi a parentFunction som returnerar en anonym funktion, och vi kallar den returnerade funktionen från det globala räckviddet. Eftersom vår anonyma funktion definierades som innehöll inuti parentFunction, det har fortfarande tillgång till parentFunctions räckvidd när det åberopas. Detta kallas en nedläggning.

Prov: sample116.html

 

Tanken att du ska ta bort här är att omfångskedjan bestäms vid definitionen bokstavligen i det sätt som koden skrivs. Om du skickar in funktioner inom din kod ändras inte omfattningskedjan.


Stängningar orsakas av omfångskedjan

Ta det du har lärt dig om omfattningen kedjan och omfånget lookup från den här artikeln, och en stängning bör inte vara alltför komplicerad att förstå. I följande exempel skapar vi en funktion som heter countUpFromZero. Den här funktionen returnerar faktiskt en referens till barnfunktionen i den. När detta barns funktion (nestad funktion) åberopas, har den fortfarande tillgång till moderfunktionens omfattning på grund av omfattningskedjan.

Prov: sample117.html

 

Varje gång countUpFromZero funktionen åberopas, den anonyma funktionen som finns i (och återvände från) countUpFromZero funktionen har fortfarande tillgång till moderfunktionens omfattning. Denna teknik, underlättad via omfattningskedjan, är ett exempel på en tillslutning.


Slutsats

Om du känner att jag har förenklade stängningar, är du troligt korrekt i den här tanken. Men jag gjorde det med avsikt eftersom jag tror att de viktiga delarna kommer från en solid förståelse av funktioner och omfattning, inte nödvändigtvis komplexiteten i exekveringssammanhang. Om du behöver ett djupt dykk i stängningar, ta en titt på JavaScript-stängningar.