2015-07-03 12 views
7

Khi tôi đọc số này answer, hãy tìm var g = f.call.bind(f);. Tôi không thể hiểu điều này với cái nhìn đầu tiên của tôi.Chaining 'bind' và 'call' trong JavaScript?

Vậy nó có ý nghĩa trực tiếp nào và có một số tình huống sử dụng thích hợp?

Và hơn nữa khi bạn sử dụng call(or apply) hoặc bind hoặc cả hai trong chuỗi, điều gì sẽ xảy ra? Có luật nào không?

+0

Bạn đã thử gọi 'g' với một số đối số và xem cách chúng có thể truy cập trong' f'? – Bergi

+0

Hãy xem [this] (http://stackoverflow.com/q/24191701/1048572). – Bergi

Trả lời

4

var g = f.call.bind(f);. Tôi không thể hiểu điều này với cái nhìn đầu tiên của tôi.

tôi giả sử bạn đang quen với cả hai phương pháp .call().bind()Function? Vâng, nó liên kết phương pháp f.call với hàm f.

Lưu ý rằng f.call chỉ là Function.prototype.call, chúng tôi không truy cập nó làm thuộc tính trên f vì chúng tôi không gọi nó ở đây.

Vậy nó có ý nghĩa trực tiếp nào?

Nó có thể trở nên rõ ràng hơn khi chúng ta nhìn vào ES6 tương đương:

(Function.prototype.call).bind(f) // or 
(f.call).bind(f) // is basically 
(...args) => f.call(...args) // or more clear 
(ctx, ...args) => f.call(ctx, ...args) 

Liệu nó có một số kịch bản sử dụng thích hợp?

Vâng, bây giờ bạn đã biết nó làm gì, vâng. Nó có thể lấy một phương thức nguyên mẫu và biến đổi nó thành một hàm tĩnh nhận lấy cá thể làm đối số đầu tiên. Một ví dụ:

function Example(n) { this.name = n; } 
Example.prototype.display = function() { console.log(this.name); } 

Example.display = Function.call.bind(Example.prototype.display); 

var e = new Example; 
e.display(); // is the same as 
Example.display(e); 

Có bất kỳ luật để biết thêm chaining call/apply/bind?

Có: như mọi khi, chỉ thuộc tính cuối cùng trong chuỗi thực sự được gọi là phương thức. Trong ví dụ trên, không có sự khác biệt giữa f.call.bind(…), Function.prototype.call.bind(…) hoặc Function.call.apply.bind.call.bind(…) - nó luôn gọi bind trên call.

Tuy nhiên, bằng cách chuyển chúng làm đối số cho nhau, bạn có thể làm somecrazythings ít nhiều hữu ích.

2

Câu hỏi hay. Hãy bắt đầu bằng cách xem xét một ví dụ xuất hiện trên StackOverflow trước: ánh xạ tất cả các chuỗi trong một mảng tới chữ thường. Tất nhiên tôi có thể viết

strings . map(function(string) { return string.toLowerCase(); }) 

nhưng điều đó có vẻ hơi dài dòng.Tôi muốn viết

strings . map(CALL_LOWERCASE_WITH_ELT_AS_THIS) 

Vì vậy, tôi có thể thử

strings . map(String.prototype.toLowerCase) 

hay, để sử dụng thành ngữ ngắn hơn một số thích

strings . map(''.toLowerCase) 

''.toLowerCase là chính xác bằng String.prototype.toLowerCase.

Nhưng điều này sẽ không làm việc, tất nhiên, bởi vì map qua mỗi phần tử với chức năng quy định như là đối số đầu tiên của nó, không phải là this của nó. Vì vậy, chúng ta cần bằng cách nào đó để chỉ định một hàm có đối số đầu tiên được sử dụng để gọi một số hàm khác là this. Đó là, tất nhiên, là chính xác những gì Function.call làm:

function.call(context) 

Đối số đầu tiên để call ("bối cảnh") được sử dụng như là this khi gọi function.

Vì vậy, sự cố được giải quyết? Chúng tôi phải có thể chỉ nói:

strings . map(''.toLowerCase.call) 

và mọi người đã thử điều này và sau đó tự hỏi tại sao nó không hoạt động. Lý do là mặc dù chúng tôi đang chuyển số call của toLowerCase làm gọi lại tới map, map vẫn không có ý tưởng rằng cuộc gọi lại được cho là được gọi với số this của ''.toLowerCase. Chúng ta cần phải nói rõ ràng mapthis sử dụng để gọi hàm, mà trong trường hợp của map chúng ta có thể làm gì với thứ hai "bối cảnh" đối số của nó:

strings . map(''.toLowerCase.call, ''.toLowerCase) 

Trên thực tế, kể từ khi call là như nhau trên bất kỳ đối tượng chức năng , chúng tôi có thể đơn giản hóa việc này thành chỉ

strings . map(Function.call, ''.toLowerCase) 

Công việc này và hoàn thành công việc một cách tuyệt vời.

Tuy nhiên, trong khi map cung cấp đối số "ngữ cảnh" thứ hai này để chỉ định this để gọi lại, đó không phải là điều chúng tôi có thể phụ thuộc vào khả dụng trong mọi tình huống. Chúng ta cần một cách tổng quát hơn để nói "thực hiện một chức năng gọi Function.call với một số chức năng cụ thể là this".

Đó chính xác là những gì bind thực hiện. Nó nói "mất một chức năng và thực hiện một chức năng mà gọi nó với một this đặc biệt":

function.bind(context) 

Trong trường hợp của chúng tôi, những gì chúng tôi muốn làm là để "mất chức năng Function.call và thực hiện một chức năng mà gọi nó với a this của ''.toLowerCase.Đó chỉ đơn giản là

Function.call.bind(''.toLowerCase) 

Bây giờ chúng ta có thể chuyển thông tin này đến map mà không cần phải sử dụng đối số thứ hai:

strings . map(Function.call.bind(''.toLowerCase)) 

đó làm việc chính xác giống như strings . map(Function.call, ''.toLowerCase), vì nói chung map(fn, ctxt) là chính xác bằng map(fn.bind(ctxt)).

Các vi phạm sau này xuống thành một dạng có thể đọc được, từng bước:

Function .   // From the Function object 
    call .    // take the `call` method 
    bind(    // and make a new function which calls it with a 'this' of 
    ''.toLowerCase // `toLowerCase` 
) 

Khi xây dựng này được xác định là một callback, chẳng hạn như để map, nó có nghĩa là:

Gọi call với đối số đầu tiên được chuyển vào và ''.toLowerCasethis, theo định nghĩa của call, có nghĩa là gọi toLowerCase với đối số đó là this.

Một số người thích để đơn giản hóa này một chút bằng cách nói

var call = Function.call; 
var toLowerCase = ''.toLowerCase; 

strings . map(call.bind(toLowerCase)) 

hay, sử dụng đối số thứ hai được cung cấp bởi map, chỉ

strings . map(call, toLowerCase) 

đó là gần như có thể đọc được như tiếng Anh: "bản đồ mỗi chuỗi đến kết quả của gọi ing toLowerCase.

Một trường hợp sử dụng phổ biến khác có liên quan sẽ chỉ định gọi lại theo số then theo lời hứa. Hãy xem xét mã sau đây:

promise . then(function(result) { result.frombulate(); }) 

Điều đó tốt, nhưng có một chút tiết. Và then không có cách nào để vượt qua trong một ngữ cảnh được sử dụng như this khi gọi trình xử lý thành công hoặc lỗi. Nhưng với việc trên, bây giờ chúng ta có thể viết:

promise . then(call.bind(frombulate)) 

Có trường hợp sử dụng khác cho call.bind thành ngữ, nhưng đây là một trong những phổ biến nhất: xác định một callback có hiệu lực thi hành là để gọi một số chức năng với các tham số được chuyển đến hàm gọi lại là this.

Với ES6 chức năng chất béo mũi tên, tất nhiên, tôi có thể viết

promise . then(result => result.frombulate()) 

do đó tương đối ít lợi thế trong tốc ký được cung cấp bởi call.bind(frombulate), và rất khó để phủ nhận rằng phiên bản chất béo mũi tên là hơn có thể đọc được bằng cách sử dụng bind.

Câu hỏi sau có thể quan tâm: Array.map and lifted functions in Javascript.

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