Chrome 中的“未捕获的 TypeError:非法调用”

Posted

技术标签:

【中文标题】Chrome 中的“未捕获的 TypeError:非法调用”【英文标题】:"Uncaught TypeError: Illegal invocation" in Chrome 【发布时间】:2012-03-29 11:55:31 【问题描述】:

当我使用requestAnimationFrame 用下面的代码做一些原生支持的动画时:

var support = 
    animationFrame: window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame
;

support.animationFrame(function() ); //error

support.animationFrame.call(window, function() ); //right

直接调用support.animationFrame会给...

未捕获的类型错误:非法调用

在 Chrome 中。为什么?

【问题讨论】:

【参考方案1】:

在您的代码中,您将本机方法分配给自定义对象的属性。 当您调用 support.animationFrame(function () ) 时,它会在当前对象的上下文中执行(即支持)。原生 requestAnimationFrame 函数要正常工作,必须在 window 的上下文中执行。

所以这里正确的用法是support.animationFrame.call(window, function() );

警报也是如此:

var myObj = 
  myAlert : alert //copying native alert to an object
;

myObj.myAlert('this is an alert'); //is illegal
myObj.myAlert.call(window, 'this is an alert'); // executing in context of window 

另一种选择是使用Function.prototype.bind(),它是 ES5 标准的一部分,适用于所有现代浏览器。

var _raf = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame;

var support = 
   animationFrame: _raf ? _raf.bind(window) : null
;

【讨论】:

从 Chrome 33 开始,第二次调用也因“非法调用”而失败。一旦答案是updated,很高兴删除反对票! @DanDascalescu:我正在使用 chrome 33,它对我有用。 我刚刚复制粘贴了您的代码并收到非法调用错误。 Here's the screencast. 你肯定会得到非法调用错误,因为第一个标签myObj.myAlert('this is an alert');是非法的。正确用法是myObj.myAlert.call(window, 'this is an alert')。请正确阅读答案并尝试理解。 如果我不是唯一一个被困在试图让 console.log.apply 以同样的方式工作的人,“这个”应该是控制台,而不是窗口:***.com/questions/8159233/… 【参考方案2】:

你也可以使用:

var obj = 
    alert: alert.bind(window)
;
obj.alert('I´m an alert!!');

【讨论】:

这并不能完全回答问题。我认为它应该是评论,而不是答案。 此外,绑定到适当的对象也很重要,例如使用 history.replaceState 时,应使用:var realReplaceState = history.replaceState.bind(history); @DeeY:感谢您回答我的问题!对于未来的人,localStorage.clear 要求您 .bind(localStorage),而不是 .bind(window) 所以,过去是let log = console.loglet create = document.createElement,现在是let log = console.log.bind(console)let create = document.createElement.bind(document)。好的好的好的。【参考方案3】:

当你执行一个方法(即分配给一个对象的函数)时,你可以在其中使用this变量来引用这个对象,例如:

var obj = 
  someProperty: true,
  someMethod: function() 
    console.log(this.someProperty);
  
;
obj.someMethod(); // logs true

如果将一个对象的方法分配给另一个对象,则其this 变量指的是新对象,例如:

var obj = 
  someProperty: true,
  someMethod: function() 
    console.log(this.someProperty);
  
;

var anotherObj = 
  someProperty: false,
  someMethod: obj.someMethod
;

anotherObj.someMethod(); // logs false

当您将windowrequestAnimationFrame 方法分配给另一个对象时,也会发生同样的事情。诸如 this 的原生函数具有内置保护,可防止在其他上下文中执行它。

有一个Function.prototype.call() 函数,它允许您在另一个上下文中调用一个函数。您只需将它(将用作上下文的对象)作为第一个参数传递给此方法。例如alert.call() 给出TypeError: Illegal invocation。但是,alert.call(window) 工作正常,因为现在 alert 在其原始范围内执行。

如果您将.call() 与您的对象一起使用:

support.animationFrame.call(window, function() );

它工作正常,因为requestAnimationFrame 是在window 范围内执行的,而不是你的对象。

但是,每次您想调用此方法时都使用.call(),这不是很优雅的解决方案。相反,您可以使用Function.prototype.bind()。它与.call() 具有类似的效果,但它不是调用函数,而是创建一个始终在指定上下文中调用的新函数。例如:

window.someProperty = true;
var obj = 
  someProperty: false,
  someMethod: function() 
    console.log(this.someProperty);
  
;

var someMethodInWindowContext = obj.someMethod.bind(window);
someMethodInWindowContext(); // logs true

Function.prototype.bind() 的唯一缺点是它是 ECMAScript 5 的一部分,is not supported in IE <= 8。还好有a polyfill on MDN。

您可能已经知道,您可以使用.bind()window 的上下文中始终执行requestAnimationFrame。您的代码可能如下所示:

var support = 
    animationFrame: (window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame).bind(window)
;

那么你可以简单地使用support.animationFrame(function() );

【讨论】:

以上是关于Chrome 中的“未捕获的 TypeError:非法调用”的主要内容,如果未能解决你的问题,请参考以下文章

未捕获的 TypeError:Vue.elementDirective 不是函数

未捕获的 TypeError - 不是函数 - 没有视觉错误

获取未捕获的 TypeError: ...default 不是构造函数 - 来自 Vue 组件

未捕获的 TypeError:无法设置 null 的属性(设置“innerHTML”)

未捕获的 TypeError:Javascript 中的非法调用(地理位置)

Javascript 中的数组错误:未捕获的 TypeError:无法读取未定义的属性“x”