2016-01-19 26 views
12

Tôi nhìn thấy trong Mozilla polyfill của fn.bind() như thế này:Nếu chúng ta polyfill fn.bind() trong JavaScript, tại sao bạn phải kiểm tra loại "this"?

if (!Function.prototype.bind) { 

    Function.prototype.bind = function(oThis) { 

    if (typeof this !== 'function') { 
     // closest thing possible to the ECMAScript 5 
     // internal IsCallable function 
     throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); 
    } 

    // other code omitted here... 

    }; 
} 

Tôi không hiểu lý do tại sao chúng ta phải kiểm tra các loại this ... bởi vì nếu chúng ta nói fn.bind()fn là một chức năng, sau đó nó sẽ công việc và nếu fn không phải là chức năng, thì fn.bind sẽ không bao giờ đạt được Function.prototype.bind bằng cách thừa kế nguyên mẫu. Vậy tại sao chúng ta phải kiểm tra loại this?

Trả lời

8

if fn is not a function, then fn.bind will never reach Function.prototype.bind by prototypal inheritance.

Đúng, tuy nhiên đây không phải là cách duy nhất this có thể được đặt. Ví dụ: nếu chúng tôi sử dụng các phương pháp .call hoặc .apply trên chính chức năng bind hoặc làm điều gì đó thực sự điên rồ như gán nó cho các đối tượng khác, nó sẽ hoạt động khác với hàm gốc.

Xét đoạn mã sau sử dụng phương pháp ES5 mẹ đẻ:

var notAFunction = {}; 
 
var someObject = {}; 
 

 
Function.prototype.bind.call(notAFunction, someObject);

này sẽ ném một TypeError, như sau:

TypeError: Function.prototype.bind called on incompatible Object

kiểm tra thêm này là cơ bản bắt chước kiểm tra sanity này từ các chức năng bản địa chặt chẽ nhất có thể.


So why do we have to check the type of this ?

Bạn không kỹ thuật phải, như trường hợp này sẽ là một lợi thế cạnh hợp cụ thể cho mã lành mạnh nhất, nhưng để làm cho polyfill hành xử-chặt chẽ với ES5 spec, nó là tốt đẹp. Vì các trình duyệt ES5 thậm chí sẽ không bao giờ chạy mã này, cũng không có hiệu năng nào cho trình duyệt hiện đại.


Hành vi này is defined in the ECMA-262 5.1 Edition specification và trở đi:

  1. Let Target be the this value.
  2. If IsCallable(Target) is false, throw a TypeError exception.
3

Không phải tất cả đối tượng có thể được gọi kế thừa từ (hoặc tương đương) Function.prototype. Ví dụ:

typeof document.createElement('object') === "function"; // true 
document.createElement('object') instanceof Function; // false 

Và ngay cả đối tượng mà kế thừa từ Function.prototype có thể có bind shadowed:

var f = function() {}; 
f.bind = 123; 
f.bind === Function.prototype.bind; // false 

Tuy nhiên, bạn có thể muốn để có thể gọi Function.prototype.bind trên các đối tượng có thể được gọi. Đó là lý do tại sao chúng tôi có những thứ như Function.prototype.call, Function.prototype.apply hoặc Reflect.apply. Bạn có thể sử dụng chúng để gọi bind trên bất kỳ đối tượng nào, cho dù đó là có thể gọi hay không.

Nghịch đảo cũng có thể. Bạn có thể có một đối tượng kế thừa từ Function.prototype và không thể gọi được.Ví dụ:

Object.create(Function.prototype) instanceof Function; // true 
typeof Object.create(Function.prototype) === "function"; // false 

Vì vậy, bạn không thể đưa ra bất kỳ giả định nào. Nếu bạn muốn polyfill phù hợp với ES5, bạn phải kiểm tra xem đối tượng có thể gọi được hay không. Trong ES5, kiểm tra xem typeof trả về "function" có thực hiện chính xác điều đó không (nhưng trong ES3 nó có thể không hoạt động đối với các đối tượng lưu trữ).

+0

Nếu ai đó thực sự vẫn cần polyfill 'Function.prototype.bind' là vì các công cụ JS cũ hơn không tuân thủ đầy đủ ES5, và sau đó hàm testof X ===" 'không nhất thiết có nghĩa là có thể gọi được . Ví dụ, trong IE8, 'typeof alert' là' "object" '. Xem thêm http://stackoverflow.com/questions/17975210 –

+0

@ GOTO0 Đúng, vật chủ là một cơn đau ở cổ, đặc biệt trên IE. – Oriol

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