2012-03-13 60 views
109

当我使用​​做一些原生支持动画与下面的代码:“遗漏的类型错误:非法调用”在Chrome

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会给...

Uncaught TypeError: Illegal invocation

在铬。为什么?

回答

153

在您的代码中,您将自定义方法分配给自定义对象的属性。 当您致电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

从Chrome 33起,第二次调用失败以及“非法调用”。一旦答案[更新](http://stackoverflow.com/a/13278323/1269037),快乐删除downvote! – 2014-03-06 13:19:44

+0

@DanDascalescu:我使用的是铬合金33,它适用于我。 – Nemoy 2014-03-10 02:30:15

+1

我刚刚复制粘贴你的代码,并得到非法的调用错误。 (http://imgur.com/oTDsazG) – 2014-03-10 03:22:47

11

还可以使用:

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

这并没有完全回答这个问题。我认为它应该是一个评论,而不是一个答案。 – 2016-01-19 22:49:32

+1

此外,绑定到适当的对象也很重要,例如,当使用history.replaceState时,应该使用: 'var realReplaceState = history.replaceState.bind(history);' – DeeY 2016-03-12 14:43:52

7

当执行的方法(即,分配给对象的功能),在其内部可以使用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​​方法到另一个对象发生。诸如此类的本机功能具有内置保护,以免在其他情况下执行它。

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

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

support.animationFrame.call(window, function() {}); 

因为​​是在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的上下文中执行​​。您的代码可能如下所示:

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

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

相关问题