Lorsque j'utilise requestAnimationFrame
pour créer des animations natives avec le code ci-dessous:
var support = {
animationFrame: window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame
};
support.animationFrame(function() {}); //error
support.animationFrame.call(window, function() {}); //right
Appeler directement le support.animationFrame
vous donnera ...
TypeError non capturé: invocation illégale
en chrome. Pourquoi?
Dans votre code, vous affectez une méthode native à une propriété d'objet personnalisé. Lorsque vous appelez support.animationFrame(function () {})
, il est exécuté dans le contexte de l'objet actuel (c'est-à-dire, support). Pour que la fonction native requestAnimationFrame fonctionne correctement, elle doit être exécutée dans le contexte de window
.
Donc, l'utilisation correcte ici est support.animationFrame.call(window, function() {});
.
La même chose arrive avec alert aussi:
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
Une autre option consiste à utiliser Function.prototype.bind () , qui fait partie du standard ES5 et est disponible dans tous les navigateurs modernes.
var _raf = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
var support = {
animationFrame: _raf ? _raf.bind(window) : null
};
Vous pouvez aussi utiliser:
var obj = {
alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
Lorsque vous exécutez une méthode (c'est-à-dire une fonction affectée à un objet), vous pouvez y utiliser la variable this
pour faire référence à cet objet, par exemple:
var obj = {
someProperty: true,
someMethod: function() {
console.log(this.someProperty);
}
};
obj.someMethod(); // logs true
Si vous affectez une méthode d'un objet à un autre, sa variable this
fait référence au nouvel objet, par exemple:
var obj = {
someProperty: true,
someMethod: function() {
console.log(this.someProperty);
}
};
var anotherObj = {
someProperty: false,
someMethod: obj.someMethod
};
anotherObj.someMethod(); // logs false
La même chose se produit lorsque vous affectez la méthode requestAnimationFrame
de window
à un autre objet. Les fonctions natives, telles que celle-ci, bénéficient d'une protection intégrée contre leur exécution dans un autre contexte.
Il existe une fonction Function.prototype.call()
, qui vous permet d'appeler une fonction dans un autre contexte. Vous devez juste le passer (l'objet qui sera utilisé comme contexte) comme premier paramètre de cette méthode. Par exemple, alert.call({})
donne TypeError: Illegal invocation
. Cependant, alert.call(window)
fonctionne bien, car désormais alert
est exécuté dans sa portée d'origine.
Si vous utilisez .call()
avec votre objet comme ça:
support.animationFrame.call(window, function() {});
cela fonctionne bien, car requestAnimationFrame
est exécuté dans le cadre de window
à la place de votre objet.
Cependant, utiliser .call()
chaque fois que vous souhaitez appeler cette méthode n'est pas une solution très élégante. Au lieu de cela, vous pouvez utiliser Function.prototype.bind()
. Son effet est similaire à celui de .call()
, mais au lieu d'appeler la fonction, il crée une nouvelle fonction qui sera toujours appelée dans un contexte spécifié. Par exemple:
window.someProperty = true;
var obj = {
someProperty: false,
someMethod: function() {
console.log(this.someProperty);
}
};
var someMethodInWindowContext = obj.someMethod.bind(window);
someMethodInWindowContext(); // logs true
Le seul inconvénient de Function.prototype.bind()
est qu’il fait partie d’ECMAScript 5, qui n’est pas pris en charge dans IE <= 8 . Heureusement, il y a n polyfill sur MDN .
Comme vous l'avez probablement déjà compris, vous pouvez utiliser .bind()
pour toujours exécuter requestAnimationFrame
dans le contexte de window
. Votre code pourrait ressembler à ceci:
var support = {
animationFrame: (window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame).bind(window)
};
Ensuite, vous pouvez simplement utiliser support.animationFrame(function() {});
.