Chromeの「Uncaught TypeError:Illegal invocation」
requestAnimationFrame
を使用して、以下のコードでネイティブサポートのアニメーションを実行する場合:
var support = {
animationFrame: window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame
};
support.animationFrame(function() {}); //error
support.animationFrame.call(window, function() {}); //right
support.animationFrame
を直接呼び出すと...
キャッチされていないTypeError:不正な呼び出し
chromeで。どうして?
コードでは、ネイティブオブジェクトをカスタムオブジェクトのプロパティに割り当てています。 support.animationFrame(function () {})
を呼び出すと、現在のオブジェクトのコンテキストで実行されます(つまり、サポート)。ネイティブのrequestAnimationFrame関数が正しく機能するには、window
のコンテキストで実行する必要があります。
したがって、ここでの正しい使用法はsupport.animationFrame.call(window, function() {});
です。
アラートでも同じことが起こります。
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
もう1つのオプションは、 Function.prototype.bind() を使用することです。これはES5標準の一部であり、最新のすべてのブラウザーで使用できます。
var _raf = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
var support = {
animationFrame: _raf ? _raf.bind(window) : null
};
以下も使用できます。
var obj = {
alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
メソッド(つまり、オブジェクトに割り当てられた関数)を実行すると、その中でthis
変数を使用してこのオブジェクトを参照できます。例:
var obj = {
someProperty: true,
someMethod: function() {
console.log(this.someProperty);
}
};
obj.someMethod(); // logs true
あるオブジェクトから別のオブジェクトにメソッドを割り当てると、そのthis
変数は新しいオブジェクトを参照します。次に例を示します。
var obj = {
someProperty: true,
someMethod: function() {
console.log(this.someProperty);
}
};
var anotherObj = {
someProperty: false,
someMethod: obj.someMethod
};
anotherObj.someMethod(); // logs false
requestAnimationFrame
のwindow
メソッドを別のオブジェクトに割り当てると、同じことが起こります。このようなネイティブ関数には、他のコンテキストでの実行からの保護が組み込まれています。
Function.prototype.call()
関数があり、別のコンテキストで関数を呼び出すことができます。このメソッドへの最初のパラメーターとして(コンテキストとして使用されるオブジェクト)を渡すだけです。たとえば、alert.call({})
はTypeError: Illegal invocation
を提供します。ただし、現在alert
は元のスコープで実行されるため、alert.call(window)
は正常に機能します。
そのようなオブジェクトで.call()
を使用する場合:
support.animationFrame.call(window, function() {});
requestAnimationFrame
はオブジェクトではなくwindow
のスコープで実行されるため、正常に動作します。
ただし、このメソッドを呼び出すたびに.call()
を使用することは、あまり洗練されたソリューションではありません。代わりに、 Function.prototype.bind()
を使用できます。 .call()
と同様の効果がありますが、関数を呼び出す代わりに、指定されたコンテキストで常に呼び出される新しい関数を作成します。例えば:
window.someProperty = true;
var obj = {
someProperty: false,
someMethod: function() {
console.log(this.someProperty);
}
};
var someMethodInWindowContext = obj.someMethod.bind(window);
someMethodInWindowContext(); // logs true
Function.prototype.bind()
の唯一の欠点は、ECMAScript 5の一部であるということです IE <= 8ではサポートされていません 。幸いなことに、 MDNのポリフィル があります。
おそらく既にわかっているように、.bind()
を使用して、常にrequestAnimationFrame
のコンテキストでwindow
を実行できます。コードは次のようになります。
var support = {
animationFrame: (window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame).bind(window)
};
その後、単にsupport.animationFrame(function() {});
を使用できます。