“ Uncaught TypeError: การร้องขอที่ผิดกฎหมาย” ใน Chrome


142

เมื่อฉันใช้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 ทำไม?

คำตอบ:


202

ในโค้ดของคุณคุณกำลังกำหนดวิธีดั้งเดิมให้กับคุณสมบัติของวัตถุที่กำหนดเอง เมื่อคุณเรียกใช้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 

อีกทางเลือกหนึ่งคือใช้Function.prototype.bind ()ซึ่งเป็นส่วนหนึ่งของมาตรฐาน ES5 และมีอยู่ในเบราว์เซอร์สมัยใหม่ทั้งหมด

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

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

1
ใน Chrome 33 การโทรครั้งที่สองล้มเหลวเช่นกันกับ "การร้องขอที่ผิดกฎหมาย" ยินดีที่จะลบการโหวตลงคะแนนเมื่อคำตอบได้รับการอัปเดต !
Dan Dascalescu

@DanDascalescu: ฉันใช้ chrome 33 และมันใช้งานได้สำหรับฉัน
Nemoy

1
ฉันเพิ่งคัดลอกรหัสของคุณและได้รับข้อผิดพลาดในการเรียกใช้ผิดกฎหมาย นี่คือ screencast
Dan Dascalescu

24
คุณจะได้รับข้อผิดพลาดในการเรียกใช้อย่างผิดกฎหมายอย่างแน่นอนเพราะการพูดติดอ่างครั้งแรกmyObj.myAlert('this is an alert');นั้นผิดกฎหมาย การใช้งานที่ถูกต้องคือmyObj.myAlert.call(window, 'this is an alert'). โปรดอ่านคำตอบอย่างถูกต้องและพยายามทำความเข้าใจ
Nemoy

3
ถ้าฉันไม่ใช่คนเดียวที่นี่ติดอยู่กับการพยายามทำให้ console.log.apply ทำงานในลักษณะเดียวกัน "นี่" ควรเป็นคอนโซลไม่ใช่หน้าต่าง: stackoverflow.com/questions/8159233/…
Alex

17

คุณยังสามารถใช้:

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

2
สิ่งนี้ไม่สามารถตอบคำถามได้ทั้งหมด ฉันคิดว่ามันควรจะเป็นความคิดเห็นไม่ใช่คำตอบ
MichałPerłakowski

2
นอกจากนี้สิ่งสำคัญคือต้องผูกเข้ากับวัตถุที่เหมาะสมเช่นเมื่อทำงานกับ history.replaceState ควรใช้: var realReplaceState = history.replaceState.bind(history);
DeeY

1
@DeeY: ขอบคุณที่ตอบคำถามของฉัน! สำหรับคนในอนาคต localStorage.clear คุณจะต้องไม่.bind(localStorage) .bind(window)
Samyok Nepal

14

เมื่อคุณเรียกใช้วิธีการ (เช่นฟังก์ชันที่กำหนดให้กับวัตถุ) ภายในนั้นคุณสามารถใช้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() {});

มันใช้งานได้ดีเพราะ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 โชคดีที่มีpolyfill บน 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.