Quando você executa um método (ou seja, função atribuída a um objeto), dentro dele, você pode usar a thisvariável para se referir a esse objeto, por exemplo:
var obj = {
someProperty: true,
someMethod: function() {
console.log(this.someProperty);
}
};
obj.someMethod(); // logs true
Se você atribuir um método de um objeto para outro, sua thisvariável se refere ao novo objeto, por exemplo:
var obj = {
someProperty: true,
someMethod: function() {
console.log(this.someProperty);
}
};
var anotherObj = {
someProperty: false,
someMethod: obj.someMethod
};
anotherObj.someMethod(); // logs false
O mesmo acontece quando você atribui o requestAnimationFramemétodo de windowa outro objeto. Funções nativas, como essa, têm proteção interna contra execução em outro contexto.
Existe uma Function.prototype.call()função que permite chamar uma função em outro contexto. Você apenas precisa passá-lo (o objeto que será usado como contexto) como um primeiro parâmetro para esse método. Por exemplo, alert.call({})dá TypeError: Illegal invocation. No entanto, alert.call(window)funciona bem, porque agora alerté executado em seu escopo original.
Se você usar .call()com seu objeto assim:
support.animationFrame.call(window, function() {});
funciona bem, porque requestAnimationFrameé executado no escopo e windownão no seu objeto.
No entanto, usar .call()sempre que quiser chamar esse método não é uma solução muito elegante. Em vez disso, você pode usar Function.prototype.bind(). Ele tem efeito semelhante ao .call(), mas, em vez de chamar a função, cria uma nova função que sempre será chamada no contexto especificado. Por exemplo:
window.someProperty = true;
var obj = {
someProperty: false,
someMethod: function() {
console.log(this.someProperty);
}
};
var someMethodInWindowContext = obj.someMethod.bind(window);
someMethodInWindowContext(); // logs true
A única desvantagem Function.prototype.bind()disso é que ele faz parte do ECMAScript 5, que não é suportado no IE <= 8 . Felizmente, há um polyfill no MDN .
Como você provavelmente já descobriu, você pode usar .bind()para executar sempre requestAnimationFrameno contexto de window. Seu código pode ficar assim:
var support = {
animationFrame: (window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame).bind(window)
};
Então você pode simplesmente usar support.animationFrame(function() {});.