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

ste*_*fan 128 javascript html5 google-chrome typeerror

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

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

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

support.animationFrame.call(window, function() {}); //right
Run Code Online (Sandbox Code Playgroud)

直接打电话给support.animationFrame意志......

未捕获的TypeError:非法调用

在Chrome中.为什么?

Nem*_*moy 182

在您的代码中,您将本机方法分配给自定义对象的属性.当您调用时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 
Run Code Online (Sandbox Code Playgroud)

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

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

var support = {
   animationFrame: _raf ? _raf.bind(window) : null
};
Run Code Online (Sandbox Code Playgroud)

  • 你肯定会得到非法的调用错误,因为第一个stamtement`myObj.myAlert('这是一个警报');`是非法的.正确的用法是`myObj.myAlert.call(窗口,'这是一个警报')`.请正确阅读答案并尝试理解. (23认同)
  • 如果我不是唯一一个试图让console.log.apply以同样方式工作的人,那么"这个"应该是控制台,而不是窗口:http://stackoverflow.com/questions/8159233/类型错误非法-调用上的控制台日志申请 (3认同)
  • 从 Chrome 33 开始,第二次调用也失败并显示“非法调用”。一旦答案[更新](http://stackoverflow.com/a/13278323/1269037),很高兴删除downvote! (2认同)

afm*_*eva 16

您还可以使用:

var obj = {
    alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
Run Code Online (Sandbox Code Playgroud)

  • 此外,绑定到适当的对象也很重要,例如在使用 history.replaceState 时,应该使用:`var realReplaceState = history.replaceState.bind(history);` (3认同)
  • 这不能完全回答问题。我认为这应该是评论,而不是答案。 (2认同)
  • @DeeY:感谢您回答我的问题!对于未来的人,localStorage.clear 要求您使用“.bind(localStorage)”,而不是“.bind(window)”。 (2认同)

Mic*_*ski 10

当您执行方法(即分配给对象的函数)时,您可以使用this变量来引用此对象,例如:

var obj = {
  someProperty: true,
  someMethod: function() {
    console.log(this.someProperty);
  }
};
obj.someMethod(); // logs true
Run Code Online (Sandbox Code Playgroud)

如果将方法从一个对象分配给另一个对象,则其this变量引用新对象,例如:

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

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

anotherObj.someMethod(); // logs false
Run Code Online (Sandbox Code Playgroud)

requestAnimationFrame方法分配window给另一个对象时会发生同样的事情.本机函数(例如此函数)具有内置保护,可以在其他上下文中执行它.

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

如果你.call()像这样使用你的对象:

support.animationFrame.call(window, function() {});
Run Code Online (Sandbox Code Playgroud)

它工作正常,因为它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
Run Code Online (Sandbox Code Playgroud)

唯一的缺点Function.prototype.bind()是它是ECMAScript 5的一部分,IE <= 8不支持.幸运的是,MDN上一个polyfill.

正如您可能已经想到的那样,您可以使用.bind()始终requestAnimationFrame在上下文中执行window.您的代码可能如下所示:

var support = {
    animationFrame: (window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame).bind(window)
};
Run Code Online (Sandbox Code Playgroud)

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