web-dev-qa-db-ja.com

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で。どうして?

123
stefan

コードでは、ネイティブオブジェクトをカスタムオブジェクトのプロパティに割り当てています。 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
};
178
Nemoy

以下も使用できます。

var obj = {
    alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
16
afmeva

メソッド(つまり、オブジェクトに割り当てられた関数)を実行すると、その中で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

requestAnimationFramewindowメソッドを別のオブジェクトに割り当てると、同じことが起こります。このようなネイティブ関数には、他のコンテキストでの実行からの保護が組み込まれています。

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() {});を使用できます。