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

"Uncaught TypeError: Illegal invocation" in Chrome

本文关键字:错误 非法 调用 类型 中的 Chrome      更新时间:2023-09-26

当我使用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会得到...

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

在铬中。为什么?

在代码中,您将本机方法分配给自定义对象的属性。当你调用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
};

您还可以使用:

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

当你执行一个方法(即分配给对象的函数(时,你可以在其中使用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方法分配给另一个对象时,也会发生同样的事情。本机函数(例如此类(具有内置保护,以防止在其他上下文中执行它。

有一个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 上有一个填充物。

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

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

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

这是一个非常常见的问题,与 JavaScript 中的绑定以及this如何工作有关——

假设我有一个像这样的函数document.write,如果我直接调用它,就像下面一样,写函数知道this指的是document对象,但是在 JavaScript 中有一个叫做隐式损失的东西,或者至少我们是这样引用它的

document.write("Hello"); // Write is aware of this

如果我为函数制作一个别名(它不是副本(,document.element到像 x 这样的变量,你可能会期望函数会像以前一样继续工作,对吧?

let x = document.write // x is just a refrence to the write function

不,函数x只是函数的别名write函数是大多数浏览器中的本机函数,这意味着它由浏览器实现。 x不知道document对象,因此调用时this的全局范围是函数的this

因此,如果您调用函数x它会给您非法调用错误,并且为了能够使用它,您必须x意识到this,这就是所谓的explicit binding,您可以使用这样的callbind使用它

x.call(document, "Hello, World!");
// Or you can use bind, which will create a new function
x.bind(document)("Hello, World!");

就我而言,解决此问题的正确提示是通过 XXX.catch(( 承诺方法正确处理错误和记录承诺。然后核心问题出现了,我可以处理它。