将 requestAnimationFrame 分配给对象变量时出现类型错误

Type Error when assigning requestAnimationFrame to object variable

我遇到了这个奇怪的 Type Error,当我尝试创建对 window.requestAnimationFrame 的本地引用时,它一直在发生。以下按预期工作并将记录 Foo:

var foo = window.requestAnimationFrame || 
    window.webkitRequestAnimationFrame || 
    window.mozRequestAnimationFrame || 
    window.msRequestAnimationFrame || 
    function(fn){ setTimeout(fn, 1000 / 24); };

foo(function(){ console.log('Foo') });

然而,当我绑定到父对象时,它不起作用并抛出 Type Error,我不得不说这让我感到困惑:

var bar = {};

bar.foo = window.requestAnimationFrame || 
    window.webkitRequestAnimationFrame || 
    window.mozRequestAnimationFrame || 
    window.msRequestAnimationFrame || 
    function(fn){ setTimeout(fn, 1000 / 24); };

bar.foo(function(){ console.log('Bar.Foo') });

即使 windowwindow.requestAnimationFrame 永远不会 undefined,调用它们也会在嵌套时引发类型错误。但是,当我比较两个变量时,它们被认为是相等的。是否有一个原因?有没有一种方法可以撕毁对对象内部正确实现的工作引用?

document.body.innerHTML += 'About to call Foo: <br />';
try {
 var foo = window.requestAnimationFrame || 
  window.webkitRequestAnimationFrame || 
  window.mozRequestAnimationFrame || 
  window.msRequestAnimationFrame || 
  function(fn){ setTimeout(fn, 1000 / 24); };
  
 foo(function(){ document.body.innerHTML += '<i>Foo</i><br />'; });
} catch(e){
 document.body.innerHTML += '<i> &gt;&gt;&gt; Error thrown when calling Foo.</i><br />';
}

document.body.innerHTML += 'About to call Bar.Foo: <br />';
try {
 var bar = {};
 bar.foo = window.requestAnimationFrame || 
  window.webkitRequestAnimationFrame || 
  window.mozRequestAnimationFrame || 
  window.msRequestAnimationFrame || 
  function(fn){ setTimeout(fn, 1000 / 24); };
 bar.foo(function(){ document.body.innerHTML += '<i>Bar.Foo</i><br />'; });
} catch(e){
 document.body.innerHTML += '<i> &gt;&gt;&gt; Error thrown when calling Bar.Foo.</i><br />';
}

document.body.innerHTML += foo === bar.foo
  ? '<strong>Foo and Bar.Foo are considered the same</strong><br />'
  : '<strong>Foo and Bar.Foo are NOT considered the same</strong><br />';
body { 
  font-family: Monaco, 'Courier MS', Coureire, mono-space;
}

嗯,requestAnimationFrame 是 DOM API 的一部分,它不是 JavaScript 对象,所以很难 确定 失败的原因。请记住:

  • JavaScript 中的函数在 this 设置为调用者对象的情况下进行分派,在您的情况下,该对象是 bar,它不具有所有属性 window有。
  • default 对象 JavaScript 函数是用 window 调度的 - 所以调用 window.alertalert 是一样的事情(严格模式除外)。

所以,它完全有可能在幕后某处使用 this。再一次,它是 "Host Object" 所以所有赌注都关闭了。

这是具有相同行为的类似 "plain" JS 代码。

window.msg = "Hello";

function foo(){
    if(this !== window) throw new TypeError("Illegal Invocation");   
    alert(this.msg);
}

foo(); // alerts hello, `this` is window
window.foo(); // alerts hello
var bar = {};
bar.foo = foo;
bar.foo(); // typeerror

解决方法是将其包装在匿名函数中或使用 bind.

试试这个

var bar = {};

bar.foo = (window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) || 
    (window.webkitRequestAnimationFrame && window.webkitRequestAnimationFrame.bind(window)) || 
    (window.mozRequestAnimationFrame && window.mozRequestAnimationFrame.bind(window)) || 
    (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window)) || 
    function(fn){ setTimeout(fn, 1000 / 24); };

bar.foo(function(){ console.log('Bar.Foo') });

这会将 *requestAnimation 的 this 设置为 window 对象

根据@poke 的评论

var bar = {};

bar.foo = (window.requestAnimationFrame || 
    window.webkitRequestAnimationFrame || 
    window.mozRequestAnimationFrame || 
    window.msRequestAnimationFrame || 
    function(fn){ setTimeout(fn, 1000 / 24); }).bind(window);

bar.foo(function(){ console.log('Bar.Foo') });