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

时间:2012-03-13 03:27:18

标签: 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

直接拨打support.animationFrame会给...

  

未捕获的TypeError:非法调用

在Chrome中

。为什么呢?

3 个答案:

答案 0 :(得分:176)

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

答案 1 :(得分:16)

您也可以使用:

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

答案 2 :(得分:9)

当您执行方法(即分配给对象的函数)时,您可以使用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() {});

它工作正常,因为requestAnimationFramewindow范围内执行而不是您的对象。

但是,每次要调用此方法时都使用.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()始终在requestAnimationFrame的上下文中执行window。您的代码可能如下所示:

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

然后您只需使用support.animationFrame(function() {});