“Uncaught TypeError: Permintaan ilegal” di Chrome

142

Ketika saya menggunakan requestAnimationFrameuntuk melakukan beberapa animasi yang didukung asli dengan kode di bawah ini:

var support = {
    animationFrame: window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame
};

support.animationFrame(function() {}); //error

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

Panggilan langsung support.animationFrameakan memberi ...

TypeError Tidak Tertangkap: Permintaan ilegal

di Chrome. Mengapa?

Stefan
sumber

Jawaban:

202

Dalam kode Anda, Anda menetapkan metode asli ke properti objek khusus. Ketika Anda memanggil support.animationFrame(function () {}), itu dijalankan dalam konteks objek saat ini (yaitu dukungan). Agar fungsi requestAnimationFrame asli berfungsi dengan baik, itu harus dijalankan dalam konteks window.

Jadi penggunaan yang benar di sini adalah support.animationFrame.call(window, function() {});.

Hal yang sama juga terjadi dengan peringatan:

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 

Pilihan lainnya adalah menggunakan Function.prototype.bind () yang merupakan bagian dari standar ES5 dan tersedia di semua browser modern.

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

var support = {
   animationFrame: _raf ? _raf.bind(window) : null
};
Nemoy
sumber
1
Mulai Chrome 33, panggilan kedua juga gagal dengan "Permintaan ilegal". Senang menghapus suara negatif setelah jawaban diperbarui !
Dan Dascalescu
@DanDascalescu: Saya menggunakan chrome 33 dan berfungsi untuk saya.
Nemoy
1
Saya baru saja menyalin-tempel kode Anda dan mendapatkan kesalahan permintaan ilegal. Ini screencastnya.
Dan Dascalescu
24
Anda pasti akan mendapatkan kesalahan pemanggilan ilegal, karena stamtement pertama myObj.myAlert('this is an alert');ilegal. Penggunaan yang benar adalah myObj.myAlert.call(window, 'this is an alert'). Silakan baca jawaban dengan benar dan cobalah untuk memahaminya.
Nemoy
3
Jika saya bukan satu-satunya di sini yang terjebak saat mencoba membuat console.log.apply berfungsi dengan cara yang sama, "ini" harusnya konsol, bukan window: stackoverflow.com/questions/8159233/…
Alex
17

Anda juga bisa menggunakan:

var obj = {
    alert: alert.bind(window)
};
obj.alert('I´m an alert!!');
afmeva
sumber
2
Ini tidak sepenuhnya menjawab pertanyaan itu. Saya pikir itu lebih baik menjadi komentar, bukan jawaban.
Michał Perłakowski
2
Juga, penting untuk mengikat ke objek yang sesuai, misalnya saat bekerja dengan history.replaceState, seseorang harus menggunakan: var realReplaceState = history.replaceState.bind(history);
DeeY
1
@DeeY: terima kasih telah menjawab pertanyaan saya! Untuk orang masa depan, localStorage.clear mengharuskan Anda melakukannya .bind(localStorage), bukan .bind(window).
Samyok Nepal
14

Saat Anda menjalankan sebuah metode (yaitu fungsi yang ditugaskan ke sebuah objek), di dalamnya Anda dapat menggunakan thisvariabel untuk merujuk ke objek ini, misalnya:

var obj = {
  someProperty: true,
  someMethod: function() {
    console.log(this.someProperty);
  }
};
obj.someMethod(); // logs true

Jika Anda menetapkan metode dari satu objek ke objek lain, thisvariabelnya merujuk ke objek baru, misalnya:

var obj = {
  someProperty: true,
  someMethod: function() {
    console.log(this.someProperty);
  }
};

var anotherObj = {
  someProperty: false,
  someMethod: obj.someMethod
};

anotherObj.someMethod(); // logs false

Hal yang sama terjadi ketika Anda menetapkan requestAnimationFramemetode windowke objek lain. Fungsi asli, seperti ini, memiliki perlindungan bawaan agar tidak mengeksekusinya dalam konteks lain.

Ada Function.prototype.call()fungsi, yang memungkinkan Anda memanggil fungsi dalam konteks lain. Anda hanya perlu meneruskannya (objek yang akan digunakan sebagai konteks) sebagai parameter pertama untuk metode ini. Misalnya alert.call({})memberi TypeError: Illegal invocation. Namun, alert.call(window)berfungsi dengan baik, karena sekarang alertdijalankan dalam lingkup aslinya.

Jika Anda menggunakan .call()dengan objek seperti itu:

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

ini berfungsi dengan baik, karena requestAnimationFramedijalankan dalam lingkup windowalih-alih objek Anda.

Namun, menggunakan .call()setiap kali Anda ingin memanggil metode ini, bukanlah solusi yang sangat elegan. Sebagai gantinya, Anda bisa menggunakan Function.prototype.bind(). Ini memiliki efek yang mirip dengan .call(), tetapi alih-alih memanggil fungsi, ini membuat fungsi baru yang akan selalu dipanggil dalam konteks yang ditentukan. Sebagai contoh:

window.someProperty = true;
var obj = {
  someProperty: false,
  someMethod: function() {
    console.log(this.someProperty);
  }
};

var someMethodInWindowContext = obj.someMethod.bind(window);
someMethodInWindowContext(); // logs true

Satu-satunya downside Function.prototype.bind()adalah bahwa ini adalah bagian dari ECMAScript 5, yang tidak didukung di IE <= 8 . Untungnya, ada polyfill di MDN .

Seperti yang mungkin sudah Anda ketahui, Anda dapat menggunakan .bind()untuk selalu mengeksekusi requestAnimationFramedalam konteks window. Kode Anda akan terlihat seperti ini:

var support = {
    animationFrame: (window.requestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        window.oRequestAnimationFrame).bind(window)
};

Kemudian Anda cukup menggunakan support.animationFrame(function() {});.

Michał Perłakowski
sumber