可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
[answered]
I'm testing my browser's fps for an html5 game.
I have this code:
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 );
})();
It gives the following error on Chrome:
Uncaught Error: TYPE_MISMATCH_ERR: DOM Exception 17
On line #27: requestAnimationFrame( step );
W3 says this error is: If the type of an object is incompatible with the expected type of the parameter associated to the object.
But I'm not actually interacting with the DOM at all, except for window
But if I remove the calling parentheses of the anonymous function assigned to step
and instead just declare that function and on a new line I put:
step();
It works.
Why is this?
Shouldn't both work the same?
回答1:
requestAnimationFrame
expects a function, but in your code, step
is not a function, it is undefined
because you don't return any value from your self-invoking function.
var step = (function(){
// this code is executed immediately,
// the return value is assigned to `step`
})();
If you remove the calling parenthesis, then step
is indeed a function.
Please see @Martin's comment to this answer. I was referring to the fact that step
is undefined
after the function is executed, but of course it is also undefined
when you invoke the function the first time.
回答2:
I see some fundamental misunderstanding of what's going on here. For example, in your first declaration:
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);
}
})();
You're creating an anonymous function, then immediately calling it and assigning the result to a variable. I don't see the point of this. The following would work equally well:
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);
};
There's no anonymous function now (well except for the little fallback function), it's just code that runs. You can apply a similar simplification to your step()
function.
回答3:
Among the issues is this (corrected):
var step = function(){
now = new Date().getTime();
hits += 1;
if( now - last >= 1000 ){
last += 1000;
console.log( "fps: "+ hits );
hits = 0;
}
requestAnimationFrame( step );
};
回答4:
I see a couple of issues. You are assigning step the return value of the anonymous function. Whereas, when you remove the parentheses. You are making step a function. Since you are not returning a value in the anonymous function, step
is undefined
. Therefore, you will get a type error. I would remove the parentheses at the end.
回答5:
Your current code essentially says "run this anonymous function and assign its return value to step
". There are two basic problems with this:
- The function doesn't return a value, so even after it runs
step
will be undefined.
- Even if the function did return a value, you are trying to use
step
inside the function the first time it runs, at which point the assignment to step
has not yet taken place.
The simplest way to fix this is what you already did, i.e., declare step
as a function and then run it on the next line:
var step = function() { ... };
step();
Or you could use a named function expression:
(function step() {
...
requestAnimationFrame( step );
})();
Which is the equivalent of:
(function () {
...
requestAnimationFrame( arguments.callee );
})();
Unfortunately IE isn't that great at named function expressions.
And also unfortunately (unfortunate from my point of view, anyway) arguments.callee
is now deprecated and won't work in strict mode.