Förstå Javascript (verkligen) Del 1 | Under Huven


Javascript är ett speciellt programmeringsspråk och det beter sig inte alls som de andra språk den liknar. För att bli en bra programmerare i Javascript krävs det att man ska verkligen förstå Javascript, hur det fungerar, och det är där den här serien om Javascript ska hjälpa till.

Javascript tolkas ”live” av ett program som någon annan har skapat för att avgöra om din Javascript kod följer syntax-reglerna och så tolkar den din kod till något som datorn förstår och avgör därigenom vad din script gör. Dessa program kallas för kompilerare.

Tänk på det så här:

  • Du har din kod med variabler, funktioner och objekt.
  • Mellan kompileraren och din kod sitter syntax-tolken.
  • Sedan har du kompileraren som skapar instruktioner till datorn av den kod syntax-tolken har översatt.

Denna syntax-tolk kan göra extra saker och det är detta som man bör vara medveten om.


Javascript == Synkront

Synkron betyder samtidig och Javascript är ett synkront språk vilket innebär att koden exekveras rad för rad (i den ordning den står skriven). Detta innebär också att Javascript är enkeltrådigt som i sin tur betyder att bara en sak exekveras åt gången.


Lexikaliskt Sammanhang

Vad är lexikaliskt sammanhang? Jo, det syftar till att det spelar roll var man skriver en specifik stycke kod. Ta exemplet med följande kod:

Det spelar alltså roll att variabeln b deklareras inuti en funktion. Det sitter lexikaliskt inuti funktion a.  Så, syntax-tolken tar beslut beroende på det lexikaliska sammanhanget, dvs. var koden är skriven. Detta kommer att vara betydelsefullt för att fullt förstå varför Javascript fungerar som den gör.

När vi pratar om den lexikaliska miljön så syftar vi på var koden är skriven och vad den omges av.


Exekverings-Kontexten

Exekverings-kontexten är en wrapper som hjälper till att sköta koden som exekveras. Det finns ofta flera lexikaliska miljöer, och vilket som exekveras för tillfället sköts av exekverings-kontexten (vilken kan innehålla saker utöver koden du skrivit, vilket vi återkommer till).


Objekt = Namn/Värde Par

Ett objekt i Javascript består av namn/värde par. Ett namn kan bara ha ett värde, men värdet kan vara ytterligare namn/värde par:

 


 

Den Globala Exekverings-Kontexten

Okej, du tycker nog att det är dags för lite kodande. Skapa ett HTML-dokument som du fäster en Javascript-fil till.

index.html:

script.js:

 

Öppna index.html i din webbläsare och öppna dess webbutvecklar-verktyg, som t.ex. Google Chrome Developer Tools. När nu script.js laddades skapades ett Globalt Exekverings-Kontext och med den en speciell variabel som heter this och en global objekt. Öppna Developer Tools ›› Console och skriv in this följt av Enter och du får då följande output:

Förstå JavaScript ›› this har värdet av det globala objektet window.

this värde = globala objektet för en webbläsare som är window.

I en webbläsare är det globala objektet window som innehåller en massa namn/värde par, vilka nu finns tillgängliga för oss. Varje ny webbläsarfönster har en egen window objekt och en egen global exekverings-kontext.

Att objektet är globalt betyder att den inte är inuti en funktion utan kan utan kan kommas åt från var som helst.

Om man nu skapar en global (utanför funktion) variabel, funktion eller objekt så kommer dessa att ”läggas till” det globala objektet window.

Vi testar det; Skriv in en variabel myVar med värdet ”Hej där!” och en funktion b() som skriver ut ”Funktion b” i Console:

…öppna därefter window eller this (som refererar till window) i Console och scrolla ner så kommer du att hitta:

Förstå JavaScript ›› window innehåller b och myVar

window innehåller b och myVar

Denna koppling blir ännu mer tydlig om använder så kallad punktnotation:

Förstå JavaScript ›› this och window refererar till b() och myVar

this och window har referenser till b() och myVar


 

Vad Sker Under Huven?

Så, nu går vi igenom vad som händer när man kör JavaScript-kod:

  • Ett globalt exekverings-kontext skapas, till vilken ››
    • En speciell variabel this skapas, vars värde blir ›› 
      • Det globala objektet window.
    • En länk till det yttre lexikala miljön skapas (vi kommer tillbaka till det här senare).
    • Din kod exekveras.

 


 

Hoisting

Vi ska nu gå igenom ett uttryck som kallas ”hoisting”. Om vi tar och kör följande kod:

…då får vi följande output:

Förstå javaScript ›› Variabeln får värdet undefined medan funktionen körs.

Variabeln får värdet undefined medan funktionen körs.

Det är detta som kallas för hoisting och betyder att under huven, i den globala exekverings-kontexten skapas plats i minnet för funktioner och variabler, där en funktion sparas i sin helhet medan en variabel ges värdet undefined och får sitt värde först senare när koden som tilldelar den ett värde exekveras.


Exekverings-Kontexter

Tillbaka till exekverings-kontexter. För varje ny lexikal miljö, dvs. funktion som exekveras, skapas ett nytt exekverings-kontext som därefter läggs på exekverings-stacken.

..okej, så vad är värdet för myVar vid en given tidpunkt?

Förstå javaScript ›› myVar finns som tre unika variabler i minnet.

myVar finns som tre unika variabler i minnet.

Ett globalt exekverings-kontext skapas och till det ett objekt och den speciella variabeln this. Till det globala objektet (som är window i en webbläsare) fästs function a(), b() och myVar till. När koden sedan exekveras rad för rad får myVar i den globala exekverings-kontexten värdet 1, varefter a() anropas till vilket ett nytt exekverings-kontext skapas i vilken myVar får värdet 3, varefter b() anropas till vilket ett nytt exekverings-kontext skapas i vilken myVar får värdet undefined då det inte tilldelas ett värde i b():s exekverings-kontext. När funktionerna exekverats poppas dom bort från stacken en efter en och till slut återgår vi till den globala exekverings-kontexten:

Förstå javaScript ›› En egen exekverings-kontext för varje ny lexikal miljö som exekveras.

En egen exekverings-kontext för varje ny lexikal miljö som exekveras.

…så myVar skapas i tre olika exekverings-kontexter och är därför tre unika variabler då dom inte kan ”störa” varandra. I andra programmeringsspråk skulle detta generera ett fatalt undantag, men inte i JavaScript.

Vi får detta resultat pga. det så kallade ”variabel scope” vilket är en variabels räckvidd, dvs. hur ”långt” man kan se den. En variabel i den globala exekverings-kontexten kan ses från var som helst (kom ihåg att… en del av exekverings-kontexten är en länk till den yttre miljön). Eftersom b() anropas från a() så är länken till den yttre miljön en länk till a() och eftersom a() finns i den globala exekverings-kontexten finns i a() en länk till den också, vilken även b() då också kan se eftersom den har en länk till den.

Komplicerat? Tja, inte egentligen om man tänker efter.

TIPS! Du kan skydda variabler från att skrivas över av andra globala variabler med samma namn i t.ex. bibliotek som du inkluderar i ditt projektgenom att skriva in dom i självexekverande funktioner:

 


Variabel Scope & Länken Till Den Yttre Miljön

Okej nu tar vi ett exempel som testar en variabels räckvidd:

…så vad kommer myVar att ha för värde? Jo, 1 för det är i den globala exekverings-kontexter b() skapades i till vilken b() har en yttre länk. b() söker efter värdet utanför sin egen exekverings-kontext genom länken till den yttre miljön som i det här fallet blir den globala exekverings-kontexten för det är där b() finns.

Om vi tar och flyttar in funktion b() in i funktion a(), vad tror du myVar får för värde när den skrivs ut i console fönstret?

Förstå javaScript ›› myVar får värdet från den yttre länken till a().

myVar får värdet från den yttre länken till a().

 


 

Scope Chain ›› Omfångskedja

När man löper genom flera länkar nedåt kalls det för ”scope chain” eller omfångskedja.

Ta exemplet nedan:

..detta skulle skapa en scope chain ända tillbaka till den globala-kontexten där myVar har ett värde (eller inte).

Men om vi har myVar deklarerad i en funktion som anropar en ny funktion på detta sätt:

..då skulle myVar få värdet 2, men inte om myVar skulle deklareras efter att c() anropas för då skulle den vara undefined pga. att den blivit skapad i minnet i exekverings-kontexten för b() (men inte tilldelats ett värde än).

Följande skulle därför ge resultatet undefined:

javaScript ›› Vi får undefined som myVar:s värde.

Vi får undefined som myVar:s värde.

Så, vad har vi lärt oss nu då?


Vid Exekvering Av JavaScript

  • Ett globalt Exekverings-Kontext skapas som fungerar som en wrapper för koden som exekveras, i vilken;
    • Ett globalt objekt skapas (window i en webbläsare).
    • En speciell variabel this skapas (vars värde är window).
      • Variabler och funktioner får en plats i minnet och fästs till det globala objektet.
    • En länk till det yttre miljön skapas (se nedan).
    • Din kod exekveras (se nedan).

 

Länk Till Yttre Miljön

DETTA ÄR VIKTIGT! Det finns alltid en länk till den globala exekverings-kontexten, direkt eller genom en omfångskedja (scope chain) och att den i första led är en länk till den lexikala miljön som funktionen/variabeln/objektet skapades i. På detta sätt hoppar syntax-tolken ner för länk-kedjan ända ner till den globala exekverings-kontexten.

Kom ihåg att Javascript är synkront vilket innebär att koden exekveras rad för rad (i den ordning den står skriven). Detta innebär också att Javascript är enkeltrådigt som i sin tur betyder att bara en sak exekveras åt gången.

 

 

Koden Exekveras

Vid exekvering skapas exekverings-kontexter för funktioner vartefter dom exekveras och läggs på exekverings-stacken.


 

Okej, nu har vi kommit en bit på väg. Jag avslutar för den här gången men vi återkommer till Javascript, och nästa gång fortsätter vi med event.

Till dess, koda smart 😉

 

Källhänvisning

Den största inspirationen har jag hämtat från Anthony Alicea och hans video ›› Javascript: Understanding the Weird Parts.

Du kan se dom första 3,5 timmarna av Javascript: Understanding the Weird Parts här.

 

Lämna en kommentar

E-postadressen publiceras inte. Obligatoriska fält är märkta *