3

[answered]

我正在为 html5 游戏测试浏览器的 fps。
我有这个代码:

var requestAnimationFrame = ( function() {
    return window.requestAnimationFrame || //Chromium 
    window.webkitRequestAnimationFrame || //Webkit
    window.mozRequestAnimationFrame || //Mozilla Geko
    window.oRequestAnimationFrame || //Opera Presto
    window.msRequestAnimationFrame || //IE Trident?
    function(callback) { //Fallback function
        window.setTimeout(callback, 1000/60);
    }
})();

var hits = 0;
var last = new Date().getTime();

var step = (function(){
    now = new Date().getTime();
    hits += 1;
    if( now - last >= 1000 ){
        last += 1000;
        console.log( "fps: "+ hits );
        hits = 0;
    }
    requestAnimationFrame( step );
})();

它在 Chrome 上给出以下错误:
Uncaught Error: TYPE_MISMATCH_ERR: DOM Exception 17
在第 27 行:requestAnimationFrame( step );

W3 说这个错误是:If the type of an object is incompatible with the expected type of the parameter associated to the object.
但我实际上并没有与 DOM 交互,除了window

但是,如果我删除分配给的匿名函数的调用括号,step而只是声明该函数并在新行上放置:
step();

有用。
为什么是这样?
两者不应该一样吗?

4

5 回答 5

11

requestAnimationFrame需要一个函数,但在您的代码中,step它不是一个函数,这是undefined因为您没有从自调用函数返回任何值。

var step = (function(){
    // this code is executed immediately, 
    // the return value is assigned to `step` 
})();

如果去掉调用括号,那么step确实是一个函数。

请参阅@Martin 对此答案的评论。我指的stepundefined 函数执行之后,当然也是undefined在你第一次调用函数的时候。

于 2011-12-21T00:04:56.513 回答
2

我看到了对这里发生的事情的一些基本误解。例如,在您的第一个声明中:

var requestAnimationFrame = ( function() {
    return window.requestAnimationFrame || //Chromium 
    window.webkitRequestAnimationFrame || //Webkit
    window.mozRequestAnimationFrame || //Mozilla Geko
    window.oRequestAnimationFrame || //Opera Presto
    window.msRequestAnimationFrame || //IE Trident?
    function(callback) { //Fallback function
        window.setTimeout(callback, 1000/60);
    }
})();

您正在创建一个匿名函数,然后立即调用它并将结果分配给一个变量。我不明白这一点。以下将同样有效:

var requestAnimationFrame = 
    window.requestAnimationFrame || //Chromium 
    window.webkitRequestAnimationFrame || //Webkit
    window.mozRequestAnimationFrame || //Mozilla Geko
    window.oRequestAnimationFrame || //Opera Presto
    window.msRequestAnimationFrame || //IE Trident?
    function(callback) { //Fallback function
        window.setTimeout(callback, 1000/60);
    };

现在没有匿名函数(除了小回退函数),它只是运行的代码。您可以对step()函数应用类似的简化。

于 2011-12-21T00:10:29.367 回答
2

其中的问题是(已更正):

var step = function(){
    now = new Date().getTime();
    hits += 1;
    if( now - last >= 1000 ){
        last += 1000;
        console.log( "fps: "+ hits );
        hits = 0;
    }
    requestAnimationFrame( step );
};
于 2011-12-21T00:12:34.237 回答
1

我看到了几个问题。您正在为 step 分配匿名函数的返回值。然而,当您删除括号时。你正在让 step 成为一个函数。由于您没有在匿名函数中返回值,因此stepis undefined. 因此,您将收到类型错误。我会删除最后的括号。

于 2011-12-21T00:07:10.690 回答
1

您当前的代码基本上说“运行这个匿名函数并将其返回值分配给step”。这有两个基本问题:

  1. 该函数不返回值,因此即使在它运行之后也step将是未定义的。
  2. 即使该函数确实返回了一个值,您也会在它第一次运行时尝试在该函数step 内部step使用,此时尚未发生对的赋值。

解决此问题的最简单方法是您已经执行的操作,即声明step为函数,然后在下一行运行它:

var step = function() { ... };
step();

或者您可以使用命名函数表达式:

(function step() {
   ...
   requestAnimationFrame( step );
})();

相当于:

(function () {
    ...
    requestAnimationFrame( arguments.callee );
})();

不幸的是,IE 并不擅长命名函数表达式

而且不幸的是(无论如何,从我的角度来看很不幸)arguments.callee现在已被弃用并且不会在严格模式下工作。

于 2011-12-21T00:21:36.690 回答