Chrome 中的“未捕获的类型错误:非法调用”

IT技术 javascript html google-chrome typeerror
2021-01-26 17:24:13

当我使用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 中。为什么?

3个回答

在您的代码中,您将本机方法分配给自定义对象的属性。当您调用 时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
};
你肯定会得到非法调用错误,因为第一个声明myObj.myAlert('this is an alert');是非法的。正确用法是myObj.myAlert.call(window, 'this is an alert')请正确阅读答案并尝试理解它。
2021-03-15 17:24:13
从 Chrome 33 开始,第二次调用也失败并显示“非法调用”。一旦答案更新,很高兴删除downvote
2021-03-23 17:24:13
@DanDascalescu:我正在使用 chrome 33,它对我有用。
2021-03-27 17:24:13
我刚刚复制粘贴了您的代码并收到了非法调用错误。这是截屏视频。
2021-03-29 17:24:13
如果我不是这里唯一一个试图让 console.log.apply 以相同方式工作的人,“这个”应该是控制台,而不是窗口:stackoverflow.com/questions/8159233/...
2021-04-07 17:24:13

您还可以使用:

var obj = {
    alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
@DeeY:感谢您回答我的问题!对于未来的人, localStorage.clear 要求您.bind(localStorage),而不是.bind(window)
2021-03-19 17:24:13
此外,绑定到适当的对象也很重要,例如在使用 history.replaceState 时,应该使用: var realReplaceState = history.replaceState.bind(history);
2021-03-27 17:24:13
所以,在过去的好日子里是let log = console.logand let create = document.createElement,现在是let log = console.log.bind(console)and let create = document.createElement.bind(document)好好好。
2021-03-27 17:24:13
这并不能完全回答问题。我认为它应该是评论,而不是答案。
2021-04-09 17:24:13

当你执行一个方法(即分配给一个对象的函数)时,你可以在其中使用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

当您将requestAnimationFrame方法分配window给另一个对象时,也会发生同样的事情本机函数(例如 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 的一部分,在 IE <= 8 中不受支持幸运的是,MDN 上一个 polyfill

正如您可能已经想到的那样,您可以使用.bind()始终requestAnimationFramewindow. 您的代码可能如下所示:

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

然后你可以简单地使用support.animationFrame(function() {});.