JavaScript报错:this.Update不是函数,已定义类成员仍出问题
解决JavaScript中setInterval导致的this指向错误
我不常使用JavaScript,但想要制作网页浏览器游戏,于是开发了一个平台游戏框架,编写了可被继承创建游戏的Project类:
export class Project { constructor() { this.player = new Player(new Vec2(16, 16)); this.level = new Level(100, 100); // create a canvas let canvas = document.createElement("canvas"); canvas.setAttribute("id", "gameCanvas"); document.body.appendChild(canvas); } UpdateCanvas() { let canvas = document.getElementById("gameCanvas"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; } Start() {} Update() {} DoFrame() { this.Update(); // render const canvas = document.getElementById("gameCanvas"); const ctx = canvas.getContext("2d"); ctx.fillStyle = "#00ff00"; ctx.fillRect(this.player.pos.x, this.player.pos.y, this.player.hitbox.x, this.player.hitbox.y); } Run() { this.Start(); setInterval(this.DoFrame, 1000 / 60); setInterval(this.UpdateCanvas, 1000 / 5); } }
随后编写了继承自Project类的YJump类:
class YJump extends Project { constructor() { super(); } Start() { console.log("Game started"); } Update() { } }
运行时DoFrame方法第一行this.Update();出现如下报错:
engine.js:143 Uncaught TypeError: this.Update is not a function at DoFrame (engine.js:143:8)
问题原因
这个错误不是继承导致的,而是setInterval调用函数时改变了this的指向。当你直接把this.DoFrame和this.UpdateCanvas传给setInterval时,函数会脱离原实例的作用域执行,此时this指向全局对象(非严格模式下为window),自然找不到属于Project实例的Update方法。
解决方法
方法1:使用箭头函数绑定this
箭头函数会继承外层作用域的this,确保函数执行时指向当前游戏实例:
Run() { this.Start(); setInterval(() => this.DoFrame(), 1000 / 60); setInterval(() => this.UpdateCanvas(), 1000 / 5); }
方法2:使用bind显式绑定this
通过bind方法将函数的this固定为当前实例:
Run() { this.Start(); setInterval(this.DoFrame.bind(this), 1000 / 60); setInterval(this.UpdateCanvas.bind(this), 1000 / 5); }
方法3:在构造函数中提前绑定方法
在Project的构造函数里提前完成方法的this绑定,避免每次调用Run时重复绑定:
export class Project { constructor() { this.player = new Player(new Vec2(16, 16)); this.level = new Level(100, 100); // 提前绑定this到当前实例 this.DoFrame = this.DoFrame.bind(this); this.UpdateCanvas = this.UpdateCanvas.bind(this); // create a canvas let canvas = document.createElement("canvas"); canvas.setAttribute("id", "gameCanvas"); document.body.appendChild(canvas); } // ... 其他方法保持不变 Run() { this.Start(); setInterval(this.DoFrame, 1000 / 60); setInterval(this.UpdateCanvas, 1000 / 5); } }
内容的提问来源于stack exchange,提问作者MESYETI
相关产品推荐
相关产品推荐

