2013-04-10 9 views
8

Tôi có một loạt các chức năng và tìm kiếm một cách ngắn gọn để gọi từng chức năng theo thứ tự.Gọi từng chức năng trong danh sách

fns = [ 
    function a() { console.log('a') }, 
    function b() { console.log('b') }, 
    function c() { console.log('c') }, 
] 

công trình này:

fns.map(function (f) { f() }) 

và vì vậy thực hiện điều này:

fns.map(function (f) { Function.call.call(f) }) 

tuy nhiên điều này làm nảy sinh một Lỗi Loại:

fns.map(Function.call.call) 

Tại sao không phải là công việc ví dụ sau ?

Trả lời

4

đang đơn giản hóa này thể hiện một vấn đề tương tự:

var x = Function.call.call; 
x(alert); 

Trong trường hợp này, một lần Function.call.call được gọi, nó sẽ không nhớ những bối cảnh mà từ đó nó có nguồn gốc (ví dụ: Function.call). Để tiết kiệm bối cảnh này, bạn có thể sử dụng này unholy xây dựng lừa:

Function.call.bind(Function.call) 

Nó trả về một chức năng mới trong đó bối cảnh Function.call là ràng buộc để chính nó, do đó tiết kiệm bối cảnh. Bạn có thể lưu cụm từ này trong một biến mới:

var callFn = Function.call.bind(Function.call); 

Bây giờ, callFn(alert) giống hệt với alert.call(). Lưu ý rằng bất kỳ đối số bổ sung nào cũng sẽ được chuyển theo đúng như vậy, vì vậy, callFn(alert, window) sẽ gọi alert.call(window). Hiểu hành vi này rất quan trọng trong các tình huống khi callFn được gọi là một phần của cuộc gọi lại như Array.forEach, nhờ đó ba đối số được chuyển trong mỗi lần lặp.

fns.forEach(callFn); 

Trong trường hợp của bạn, không ai trong số các chức năng bên trong fns đang sử dụng các đối số được thông qua, nhưng đằng sau hậu trường chúng được gọi như thế này:

fns[0].call(0, fns) 

Vì vậy this bằng chỉ số của phần tử (tức là Number(0)) và arguments[0] tương đương với mảng chức năng. Người quan sát quan tâm có thể nhận thấy rằng giá trị của phần tử rơi vào giữa các vết nứt, mặc dù nó vẫn có thể được tham chiếu bằng cách sử dụng arguments[0][this] hoặc, cách khác, arguments.callee (không được chấp nhận).

+0

Bingo, đúng vậy! Cảm ơn nhiều. – georg

+0

Theo dõi tốt về các đối số, nhưng tại sao 'callee'? Có gì sai với chỉ đơn giản là 'đối số [0] [này]'? – georg

+0

@ thg435 Đó là một điểm tốt; mà sẽ làm việc trong trường hợp này :) cập nhật, với cảm ơn! –

5
for (var i = 0, len = fns.length; i < len; i++) { 
    fns[i].call(); 
}; 

Đây là số đang hoạt động fiddle.

Sử dụng Function.prototype.callFunction.prototype.apply như trên để gọi hàm. Với callapply bạn có thể vượt qua phạm vi thực hiện và arguments cho cuộc gọi chức năng. Nếu bạn không cần điều đó, bạn chỉ có thể làm:

for (var i = 0, len = fns.length; i < len; i++) { 
     fns[i](); 
}; 

Về mã của bạn:

Array.prototype.map là một chức năng mà sẽ đưa callbackscope as parameters. Trong hai ví dụ đầu tiên, bạn đang sử dụng một hàm ẩn danh là callback và bạn gọi tham số được truyền tự động theo Array.prototype.map. Để mở rộng, mã của bạn tương đương với điều này:

function single(element) { 
    element(); 
}; 
fns.map(single); 

Vì vậy, ở trên là hoàn toàn chính xác. Theo nguyên tắc tương tự bằng cách sử dụng Function.call.call, bạn đang gọi hàm f được truyền dưới dạng tham số theo bản đồ.

Nhưng trong ví dụ thứ ba của bạn, bạn đang buộc một trực tiếp call qua Function.call.prototype.call, tuy nhiên trong trường hợp này, f không còn được thông qua như một tham số, có nghĩa là bạn Function.call.call sẽ cố gắng gọi undefined, do đó bạn sẽ có được TypeError. Khi bạn đặt Function.call.call bên trong map(), bạn là NOT chuyển một số callback làm đối số.

call sẽ ngay lập tức. Function.call.call là chính xác giống như Function.call.call() hoặc Function.call.call(undefined), sẽ được đánh giá ngay lập tức khi được sử dụng như bạn đã làm trong ví dụ thứ ba của mình.

+3

OP nhiều khả năng đã biết tất cả điều này. Câu hỏi đặt ra * tại sao *, không * cách *. – Jon

+0

Tôi đã thực sự viết lên như vậy trong thời gian chờ đợi! +1 – Jon

3

TypeError xảy ra vì Function.prototype.call gọi nội bộ this (với ngữ cảnh và thông số đã cho, nhưng điều đó không quan trọng trong cuộc thảo luận).

Hãy viết lại làm việc

fns.map(function (f) { Function.call.call(f) }) 

như tương đương

fns.map(function (f) { 
    var invoker = Function.call; 
    invoker.call(f); 
}) 

Nó bây giờ đã rõ ràng rằng invoker được gọi với f như this. Khi nó cố gắng gọi nội bộ theo số this thì chức năng của bạn sẽ được gọi, như mong đợi.

Bây giờ xem xét điều này:

fns.map(Function.call.call); 

và các hình thức tương đương

fns.map(function (f) { 
    var invoker = Function.call; 
    invoker.call(); 
}) 

Nó nên được rõ ràng rằng ở đây, khi invoker được gọi thisundefined; do đó nó không thể được gọi ra chính nó và điều này cho phép tăng lên TypeError.