Three.js requestAnimationFrame调用类方法报undefined错误如何解决
错误原因
该报错的本质是JavaScript函数执行上下文(this)丢失:
- ES 类的方法默认运行在严格模式下,当你将
this.animate作为回调传入requestAnimationFrame时,实际是把类原型上的animate方法脱离实例单独传递,方法被requestAnimationFrame调度执行时,内部的this不再指向类实例,会变为undefined。执行到方法内requestAnimationFrame(this.animate)这行时,尝试读取undefined的animate属性,就会抛出题目中的类型错误。 - 现有代码还存在三个会导致后续运行异常的隐藏问题:
- 给window绑定resize事件时传入的
this.onWindowResize同样存在this丢失问题,事件触发时回调内的this会指向window对象,而非类实例 onWindowResize方法内直接使用camera、renderer变量,没有通过this访问实例上的属性,就算this绑定正确也会触发变量未定义的错误- 现有代码没有主动调用animate方法的逻辑,就算修复了this问题,渲染循环也不会自动启动
- 给window绑定resize事件时传入的
修复方案
以下两种方案可任选其一实现修复:
方案1:构造函数内手动绑定this(兼容性最佳)
在构造函数中通过bind方法强制将回调函数的this固定到当前类实例,同时修正resize回调的属性访问逻辑,补全渲染循环启动调用:
export class BEngine { constructor(doc){ // 固定回调方法的this指向当前实例 this.animate = this.animate.bind(this); this.onWindowResize = this.onWindowResize.bind(this); this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); doc.body.appendChild(this.renderer.domElement); window.addEventListener("resize", this.onWindowResize, false); // 启动渲染循环 this.animate(); } animate() { requestAnimationFrame(this.animate); this.renderer.render(this.scene, this.camera); } onWindowResize() { // 通过this访问实例属性 this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); } }
方案2:箭头函数定义回调方法(写法更简洁)
箭头函数没有自身的this上下文,会直接继承定义位置外层的this(即类实例),从根源避免this丢失问题:
export class BEngine { constructor(doc){ this.scene = new THREE.Scene(); this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(window.innerWidth, window.innerHeight); doc.body.appendChild(this.renderer.domElement); window.addEventListener("resize", this.onWindowResize, false); // 启动渲染循环 this.animate(); } // 箭头函数定义方法,固定this指向实例 animate = () => { requestAnimationFrame(this.animate); this.renderer.render(this.scene, this.camera); } onWindowResize = () => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(window.innerWidth, window.innerHeight); } }
内容的提问来源于stack exchange,提问作者Benji
相关产品推荐
相关产品推荐

