Chromeで「キャッチされていないTypeError:不正な呼び出し」


136

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意志を直接呼び出すと...

Uncaught TypeError:不正な呼び出し

Chromeで。どうして?

回答:


194

コードでは、カスタムメソッドのプロパティにネイティブメソッドを割り当てています。を呼び出すと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 

別のオプションは、ES5標準の一部であり、すべての最新のブラウザーで使用できるFunction.prototype.bind()を使用することです。

var _raf = window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame;

var support = {
   animationFrame: _raf ? _raf.bind(window) : null
};

1
Chrome 33以降、2番目の呼び出しも「無効な呼び出し」で失敗します。回答が更新されたら、反対票を削除してください。
Dan Dascalescu 2014年

@DanDascalescu:私はChrome 33を使用しています。
Nemoy、2014年

1
コードをコピーして貼り付けただけで、Illegal invocation errorが発生しました。これがスクリーンキャストです。
Dan Dascalescu 2014年

24
最初の制限myObj.myAlert('this is an alert');は違法であるため、間違いなく違法な呼び出しエラーが発生します。正しい使い方はmyObj.myAlert.call(window, 'this is an alert')です。答えを正しく読んで理解してください。
Nemoy、2014年

3
ここで、console.log.applyを同じように機能させようとしているのが私だけではない場合、「これ」はウィンドウではなくコンソールにする必要があります。stackoverflow.com
Alex

17

次のものも使用できます。

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

2
これは質問に完全に答えるものではありません。むしろ答えではなくコメントにすべきだと思います。
のMichałPerłakowski

2
また、history.replaceStateで作業する場合、1が使用するなど、適切なオブジェクトにバインドすることが重要である: var realReplaceState = history.replaceState.bind(history);
DeeY

@DeeY:私の質問に答えてくれてありがとう!将来の人々にとって、localStorage.clearは.bind(localStorage)、ではなくを要求します.bind(window)
Samyokネパール、

13

メソッド(つまり、オブジェクトに割り当てられた関数)を実行すると、その内部で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.call(window)ため、正常に機能しalertます。

その.call()ようなオブジェクトで使用する場合:

support.animationFrame.call(window, function() {});

オブジェクトrequestAnimationFramewindow代わりにのスコープで実行されるため、正常に動作します。

ただし、.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() {});

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.