将requestAnimationFrame作为对象属性调用时触发非法调用错误
解决Uncaught TypeError: Illegal invocation的问题
这个报错我之前踩过坑,核心原因就是方法的this指向不符合要求!咱们一步步捋清楚:
为什么会报错?
window.requestAnimationFrame是属于window对象的原生方法,它在执行时要求内部的this必须指向window。当你直接把它赋值给foo.requestAnimationFrame后,调用foo.requestAnimationFrame(foo.draw)时,这个方法内部的this就变成了foo对象,而不是原本的window——浏览器不允许这种"非法调用"原生API的操作,所以就抛出了Uncaught TypeError: Illegal invocation。
另外,你代码里还有个小错误:foo.requestAnimationFrame = window.requestAnimationFrame();这里多了括号!这会把调用requestAnimationFrame后的返回值(一个请求ID)赋值给属性,而不是方法本身,这肯定会出问题,得去掉括号。
几种可行的解决方案
方案1:用bind固定this指向
通过bind(window)生成一个新函数,让它内部的this永远指向window,同时如果你的draw方法需要用到foo的属性,也要给draw绑定this到foo:
var foo = { draw: function() { // 这里可以正常使用this访问foo的属性 console.log("执行绘制逻辑", this); // 如果需要循环调用,记得再次绑定 this.requestAnimationFrame(this.draw.bind(this)); }, init: function() { // 绑定requestAnimationFrame的this到window,不要加括号! this.requestAnimationFrame = window.requestAnimationFrame.bind(window); // 调用时绑定draw的this到foo this.requestAnimationFrame(this.draw.bind(this)); } }; foo.init();
方案2:调用时用call/apply指定this
如果不想修改requestAnimationFrame的绑定,也可以在调用时用call直接指定this为window:
var foo = { draw: function() { console.log("绘制中"); }, init: function() { // 直接赋值方法引用,不要加括号 this.requestAnimationFrame = window.requestAnimationFrame; // 用call指定this为window,第二个参数是回调函数 this.requestAnimationFrame.call(window, this.draw.bind(this)); } }; foo.init();
方案3:直接调用window.requestAnimationFrame(最简单)
如果没必要把requestAnimationFrame挂载到foo上,直接用原生的调用方式最省心,完全不会有this的问题:
var foo = { draw: function() { console.log("执行绘制"); // 循环调用时直接用window的方法,同时绑定draw的this window.requestAnimationFrame(this.draw.bind(this)); }, init: function() { window.requestAnimationFrame(this.draw.bind(this)); } }; foo.init();
内容的提问来源于stack exchange,提问作者totoaussi
相关产品推荐
相关产品推荐

