Förstå JavaScript ”detta” nyckelord

By rik

Vad innebär nyckelordet ”this” i JavaScript? Hur kan det användas praktiskt i dina JavaScript-program? Dessa frågor är vanligt förekommande bland både nybörjare och erfarna JavaScript-utvecklare.

Om du tillhör dem som undrar över innebörden av ”this”, är den här artikeln skriven för dig. Här kommer du att utforska vad ”this” refererar till i olika situationer och lära dig några vanliga fallgropar för att undvika förvirring och fel i din kod.

”this” i global kontext

När ”this” används i ett globalt sammanhang, det vill säga utanför en funktion, kommer det att referera till fönsterobjektet. Global kontext innebär att ”this” inte befinner sig inuti en funktion.

if(true) {
  console.log(this) 
}
let i = 2
while(i < 10) {
  console.log(this) 
  i++
}

Om du kör ovanstående kod kommer du att få fönsterobjektet som resultat.

”this” inuti funktioner (metoder)

Inuti en funktion refererar ”this” till objektet som funktionen är kopplad till. Ett undantag är om ”this” används i en fristående funktion, då det refererar till fönsterobjektet. Låt oss titta på några exempel.

I exemplet nedan finns funktionen sayName inuti objektet ’me’, vilket gör den till en metod. I sådana fall refererar ”this” till det objekt som innehåller metoden.

 
function sayName() {
  return `Mitt namn är ${this.name}`
}

const me = {
  name: "Kingsley",
  sayName: sayName
}

console.log(me.sayName()) 

”this” refererar till objektet ’me’, så `this.name` inuti metoden `sayName` är exakt samma som `me.name`.

Ett annat sätt att se det är att det som står till vänster om funktionen när den anropas, det är vad ”this” kommer att referera till. Det betyder att du kan återanvända `sayName`-funktionen i olika objekt och ”this” kommer att hänvisa till en ny kontext varje gång.

Som tidigare nämnts returnerar ”this” fönsterobjektet när det används i en fristående funktion. Detta beror på att en fristående funktion som standard är kopplad till fönsterobjektet:

function talk() {
  return this
}

talk()

Att anropa `talk()` är detsamma som att anropa `window.talk()`, och allt som finns till vänster om funktionen blir automatiskt ”this”.

Ett tillägg: Nyckelordet ”this” beter sig annorlunda i JavaScripts strikta läge (strict mode), där det returnerar `undefined`. Detta är viktigt att tänka på när du använder UI-bibliotek som använder strikt läge (t.ex. React).

Använda ”this” med `Function.bind()`

Det kan uppstå situationer där du inte kan lägga till en funktion som en metod till ett objekt (som i föregående avsnitt).

Kanske objektet inte är ditt och du får det från ett bibliotek. Eller så är objektet oföränderligt och du kan inte modifiera det. I sådana fall kan du fortfarande använda funktionen separat från objektet med metoden `Function.bind()`.

I exemplet nedan är funktionen `sayName` inte en metod på objektet `me`, men du kan ändå binda den med metoden `bind()`:

function sayName() {
  return `Mitt namn är ${this.name}`
}

const me = {
  name: "Kingsley"
}

const meTalk = sayName.bind(me)

meTalk()

Det objekt som skickas till `bind()` kommer att användas som värdet för ”this” i funktionsanropet.

Sammanfattningsvis kan du använda `bind()` på vilken funktion som helst och skicka in ett nytt sammanhang (ett objekt). Detta objekt kommer att överskriva betydelsen av ”this” inuti funktionen.

Använda ”this” med `Function.call()`

Vad händer om du inte vill returnera en ny funktion, utan bara vill anropa funktionen efter att ha bundit den till ett sammanhang? Lösningen är metoden `call()`:

function sayName() {
  return `Mitt namn är ${this.name}`
}

const me = {
  name: "Kingsley"
}

sayName.call(me)

Metoden `call()` kör funktionen omedelbart istället för att returnera en ny funktion.

Om funktionen kräver en parameter kan du skicka den via `call()`. I exemplet nedan skickas språket till `sayName()`-funktionen, så att olika meddelanden kan returneras beroende på språket:

function sayName(lang) {
  if (lang === "en") {
    return `My name is ${this.name}`
  } else if (lang === "it") {
    return `Io sono ${this.name}`
  }
}

const me = {
  name: "Kingsley"
}

sayName.call(me, 'en')
sayName.call(me, 'it')

Som du ser kan du skicka vilka parametrar du vill till funktionen som andra argument till metoden `call()`. Du kan skicka så många parametrar som behövs.

Metoden `apply()` liknar `call()` och `bind()`. Den enda skillnaden är att du skickar flera argument med ett kommatecken med `call()`, medan du skickar dem i en array med `apply()`.

Sammanfattningsvis kan du med `bind()`, `call()` och `apply()` anropa funktioner med ett helt annat objekt utan att ha någon koppling mellan dem (dvs. funktionen är inte en metod på objektet).

”this” inuti konstruktorfunktioner

Om du anropar en funktion med nyckelordet `new` skapas ett objekt som ”this” refererar till och det objektet returneras:

function person(name){
  this.name = name
}

const me = new person("Kingsley")
const her = new person("Sarah")
const him = new person("Jake")

me.name
her.name
him.name

I ovanstående kod skapades tre olika objekt med samma funktion. Nyckelordet `new` skapar automatiskt en koppling mellan det nyskapade objektet och nyckelordet ”this” i funktionen.

”this” inuti callback-funktioner

Callback-funktioner skiljer sig från vanliga funktioner. En callback-funktion är en funktion som skickas som ett argument till en annan funktion, så att den kan köras när huvudfunktionen är klar.

Nyckelordet ”this” hänvisar till ett annat sammanhang när det används i callback-funktioner:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }, 1000)
}

const me = new person("Kingsley")

Efter en sekund från att `person`-konstruktorfunktionen anropats och ett nytt objekt `me` skapats, loggas fönsterobjektet som värdet av ”this”. Alltså, i en callback-funktion hänvisar ”this” till fönsterobjektet och inte det ”konstruerade” objektet.

Det finns två sätt att lösa det här. Det första är att använda `bind()` för att binda `person`-funktionen till det nyskapade objektet:

function person(name){
  this.name = name
  setTimeout(function() {
    console.log(this)
  }.bind(this), 1000)
}

const me = new person("Kingsley")

Med ovanstående ändring kommer ”this” i callbacken att peka på samma ”this” som konstruktorfunktionen (objektet `me`).

Det andra sättet att lösa problemet med ”this” i callback-funktioner är att använda pilfunktioner.

”this” inuti pilfunktioner

Pilfunktioner skiljer sig från vanliga funktioner. Du kan använda en pilfunktion för din callback. Med pilfunktioner behöver du inte längre använda `bind()` eftersom de automatiskt binds till det nyskapade objektet:

function person(name){
  this.name = name
  setTimeout(() => {
    console.log(this)
  }, 1000)
}

const me = new person("Kingsley")

Lär dig mer om JavaScript

Du har nu lärt dig om nyckelordet ”this” och vad det innebär i olika sammanhang i JavaScript. Om du är nybörjare inom JavaScript är det en stor fördel att lära sig alla grunderna i JavaScript och hur de fungerar.