2012-03-13 24 views
109

Khi tôi sử dụng requestAnimationFrame để làm một số bản địa hỗ trợ hình ảnh động với mã dưới đây:"của router Lỗi Loại: gọi trình bất hợp pháp" trong Chrome

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

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

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

Trực tiếp gọi support.animationFrame sẽ cung cấp cho ...

Uncaught TypeError: Illegal invocation

trong Chrome. Tại sao?

Trả lời

153

Trong mã của bạn, bạn đang gán một phương thức gốc cho thuộc tính của đối tượng tùy chỉnh. Khi bạn gọi support.animationFrame(function() {}), nó được thực hiện trong ngữ cảnh của đối tượng hiện tại (nghĩa là hỗ trợ). Đối với hàm requestAnimationFrame gốc để hoạt động đúng, nó phải được thực hiện trong ngữ cảnh của window.

Vì vậy, việc sử dụng chính xác ở đây là support.animationFrame.call(window, function() {});.

Các tương tự xảy ra với cảnh báo quá:

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 

Một lựa chọn khác là sử dụng Function.prototype.bind() là một phần của tiêu chuẩn ES5 và có sẵn trong tất cả các trình duyệt hiện đại.

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

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

Kể từ Chrome 33, cuộc gọi thứ hai cũng không thành công với "Yêu cầu bất hợp pháp". Hạnh phúc để loại bỏ các downvote khi câu trả lời là [cập nhật] (http://stackoverflow.com/a/13278323/1269037)! –

+0

@DanDascalescu: Tôi đang sử dụng chrome 33 và nó đang hoạt động cho tôi. – Nemoy

+1

Tôi vừa sao chép mã của bạn và nhận được lỗi yêu cầu bất hợp pháp. [Đây là screencast.] (Http://imgur.com/oTDsazG) –

11

Bạn cũng có thể sử dụng:

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

Điều này không hoàn toàn trả lời câu hỏi. Tôi nghĩ rằng nó nên được một bình luận, không phải là một câu trả lời. –

+1

Ngoài ra, điều quan trọng là phải ràng buộc với đối tượng thích hợp, ví dụ: khi làm việc với history.replaceState, bạn nên sử dụng: 'var realReplaceState = history.replaceState.bind (history);' – DeeY

7

Khi bạn thực hiện một phương pháp (tức là chức năng được gán cho một đối tượng), bên trong nó, bạn có thể sử dụng this biến để đề cập đến đối tượng này, ví dụ:

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

Nếu bạn gán một phương pháp từ một đối tượng khác, biến this của nó đề cập đến đối tượng mới, ví dụ:

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

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

 
anotherObj.someMethod(); // logs false

Điều tương tự cũng xảy ra khi bạn gán requestAnimationFrame phương pháp window đối tượng khác . Các hàm nguyên gốc, chẳng hạn như này, có tính năng bảo vệ tích hợp để thực hiện nó trong ngữ cảnh khác.

Có chức năng Function.prototype.call(), cho phép bạn gọi hàm trong ngữ cảnh khác. Bạn chỉ cần truyền nó (đối tượng sẽ được sử dụng làm ngữ cảnh) làm tham số đầu tiên cho phương thức này. Ví dụ: alert.call({}) cho TypeError: Illegal invocation. Tuy nhiên, alert.call(window) hoạt động tốt, bởi vì bây giờ alert được thực hiện trong phạm vi ban đầu của nó.

Nếu bạn sử dụng .call() với đối tượng của bạn như thế:

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

nó hoạt động tốt, vì requestAnimationFrame được thực hiện trong phạm vi window thay vì đối tượng của bạn.

Tuy nhiên, sử dụng .call() mỗi khi bạn muốn gọi phương pháp này, không phải là giải pháp rất thanh lịch.Thay vào đó, bạn có thể sử dụng Function.prototype.bind(). Nó có hiệu ứng tương tự như .call(), nhưng thay vì gọi hàm, nó tạo ra một hàm mới sẽ luôn được gọi trong ngữ cảnh được chỉ định. Ví dụ:

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

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

Nhược điểm duy nhất của Function.prototype.bind() là nó là một phần của ECMAScript 5, is not supported in IE <= 8. May mắn thay, có a polyfill on MDN.

Như bạn có thể đã tìm ra, bạn có thể sử dụng .bind() để luôn thực hiện requestAnimationFrame trong ngữ cảnh window. Mã của bạn có thể trông giống như sau:

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

Sau đó, bạn có thể chỉ cần sử dụng support.animationFrame(function() {});.

Các vấn đề liên quan