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.log
和let 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
当您将window
的requestAnimationFrame
方法分配给另一个对象时,也会发生同样的事情。诸如 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”)