html - nästlad funktion kan inte hitta sig själv i Typescript
Jag har följande kod:
//Main Entry Point.
start() {
this.init();
this.gameLoop();
}
//Init, runs only once.
init() {
let initalEntity1: Entity = new Entity(10, 10, Gender.female);
let initalEntity2: Entity = new Entity(60, 60, Gender.male);
console.log("There are " + this.Entities.length + " Items in the entities Array")
this.Entities.push(initalEntity1, initalEntity2);
console.log("There are " + this.Entities.length + " Items in the entities Array")
}
gameLoop() {
console.log("Performing a Game Loop");
requestAnimationFrame(this.gameLoop);
//MAIN LOOP THROUGH ENTITIES
for (let i in this.Entities) {
this.Render.drawEntitysToScreen(this.Entities[i].EntityPosition, this.Entities[i].gender);
}
}
Den går in i start() utan problem, och utför också all funktionalitet i init(). Därefter fortsätter den till gameloop() som den kommer att köra en gång, men raden requestAnimationFrame(this.gameLoop); som är avsedd att trigga funktionen att köras igen som en Canvas-ram orsakar följande fel:
TypeError: this är odefinierad
försöker requestAnimationFrame(gameLoop); men det får TypeScript-kompilatorn att bli upprörd...
Svar #1
Detta beror på hur this-bindning fungerar i JavaScript. Sättet på vilket du skickar this.gameLoop till requestAnimationFrame är i princip att skicka en obunden gameLoop-funktion, och när den anropas har den därför förlorat referensen till sitt this.
Det finns ett antal möjliga lösningar på detta problem:
Du kan binda
this.gameLooptillthisinne i klasskonstruktören, på följande sätt:constructor() { this.gameLoop = this.gameLoop.bind(this); }Du kan binda
this.gameLooptillthissom en del avgameLoop-metodddefinitionen. Istället för att definieragameLoopsomgameLoop() {Om du istället använder
gameLoop = () => {det kommer automatiskt att bindas till
this. Detta är en egenskap av att använda fet pil för funktionsdeklarationer: det utför automatiskt bindning tillthissom finns vid funktionsdeklarationen.Du kan ändra hur du skickar
gameLooptillrequestAnimationFrame:requestAnimationFrame(() => this.gameLoop());Detta utnyttjar återigen den automatiska
this-bindningen som utförs av pilfunktionen, men istället för att göra det som en del av klassmetoddeklarationen kan du helt enkelt göra det senare vid den tidpunkt du behöver att den ska bindas.Observera dock att att göra det på detta sätt innebär att en ny funktion kommer att skapas varje gång
gameLoopanropas.
Svar #2
När du anropar this.gameLoop() inom start, kommer värdet för this inuti gameLoop's kropp att vara den klass gameLoop tillhör, eftersom du anropar gameLoop som en egenskap av this (klassen).
När du skickar en funktionsreferens kan dess värde för this vara vad som helst när funktionen anropas från någon annanstans.
Lösning 1 | Använda Function.prototype.bind
“bind värdet för this när du ger funktionen till requestAnimationFrame. Genom att göra detta säger du uttryckligen:
Låt argumentet till bind vara this inom varje anrop till gameLoop, oavsett hur det anropas eller varifrån det anropas.
requestAnimationFrame(this.gameLoop.bind(this));
Observera att bind returnerar en ny funktion, så den ursprungliga gameLoop-funktionen som fortfarande är en egenskap i din klass förblir oförändrad.
Lösning 2 | Använda en pilfunktion
Definiera en pilfunktion för att slutligen utföra anropet till gameLoop istället för requestAnimationFrame. this-värdet inom pilfunktioner är statiskt och ärvt från exekveringskontexten som omger funktionsdeklarationen.
requestAnimationFrame(() => this.gameLoop());
