2008-11-26 23 views
52

Tôi đang cố gắng viết một hàm JavaScript sẽ trả về đối số (hàm) đầu tiên của nó với tất cả các đối số còn lại của nó như các tham số được đặt trước cho hàm đó.Làm cách nào để đặt trước các đối số trong lệnh gọi hàm JavaScript? (Ứng dụng chức năng một phần)

Vì vậy:

function out(a, b) { 
    document.write(a + " " + b); 
} 

function setter(...) {...} 

setter(out, "hello")("world"); 
setter(out, "hello", "world")(); 

Would đầu ra "hello world" hai lần. đối với một số việc thực hiện setter

Tôi đã gặp sự cố với thao tác mảng đối số trong lần thử đầu tiên của tôi, nhưng có vẻ như sẽ có cách tốt hơn để thực hiện việc này.

+0

Tôi đề xuất chức năng 'curry' của [Lodash] (http://lodash.com/docs#curry). BTW Tôi khuyên bạn nên toàn bộ thư viện, nó rất hữu ích. –

Trả lời

96

Trước hết, bạn cần một phần - there is a difference between a partial and a curry - và ở đây là tất cả các bạn cần, mà không có một khuôn khổ:

function partial(func /*, 0..n args */) { 
    var args = Array.prototype.slice.call(arguments, 1); 
    return function() { 
    var allArguments = args.concat(Array.prototype.slice.call(arguments)); 
    return func.apply(this, allArguments); 
    }; 
} 

Bây giờ, sử dụng ví dụ của bạn, bạn có thể thực hiện chính xác những gì bạn đang sau:

partial(out, "hello")("world"); 
partial(out, "hello", "world")(); 

// and here is my own extended example 
var sayHelloTo = partial(out, "Hello"); 
sayHelloTo("World"); 
sayHelloTo("Alex"); 

Các partial() chức năng có thể được sử dụng để thực hiện, nhưng không phải là currying. Dưới đây là một trích dẫn từ a blog post on the difference:

đâu ứng dụng phần mất một chức năng và từ đó xây dựng một chức năng mà có lẽ ít hơn, currying xây dựng các chức năng mà mất nhiều tranh cãi bởi thành phần của chức năng mà mỗi mất một đối số duy nhất.

Hy vọng rằng sẽ giúp.

+0

Bạn có thể chỉnh sửa điều này không? func.apply (this, allArguments) cần phải trả về func.apply (this, allArguments) – AlexH

+0

Xong - không thực sự nhìn xa hơn chức năng ví dụ không trả về của bạn, vì vậy tôi đã bỏ qua nó ban đầu. Cảm ơn. –

+0

Tôi đang bỏ phiếu cho câu trả lời của bạn chỉ vì tôi đã tìm kiếm một cái gì đó để làm điều này trước đây. –

3

Có phải là curried javascript những gì bạn đang tìm kiếm không?

+0

Tôi không nghĩ rằng anh ấy cần cà ri, mỗi lần - xem http://stackoverflow.com/questions/218025/what-is-the-difference-between-currying-and-partial-application –

+1

Liên kết không còn hoạt động nữa. Đây có phải là cùng một trang không? http://www.crockford.com/javascript/www_svendtofte_com/code/curried_javascript/index.html –

2

Nếu bạn sử dụng Dojo bạn chỉ cần gọi dojo.hitch() gần như chính xác những gì bạn muốn. Hầu như — bởi vì nó có thể được sử dụng để đóng gói bối cảnh là tốt. Nhưng ví dụ của bạn là đầu tiên:

dojo.hitch(out, "hello")("world"); 
dojo.hitch(out, "hello", "world")(); 

Cũng như:

var A = { 
    sep: ", ", 
    out: function(a, b){ console.log(a + this.sep + b); } 
}; 

// using functions in context  
dojo.hitch(A, A.out, "hello")("world"); 
dojo.hitch(A, A.out, "hello", "world")(); 

// using names in context 
dojo.hitch(A, "out", "hello")("world"); 
dojo.hitch(A, "out", "hello", "world")(); 

dojo.hitch() là một phần của cơ sở Dojo, vì vậy ngay sau khi bạn bao gồm dojo.js nó ở đó cho bạn.

Một cơ sở chung khác có sẵn trong mô-đun dojox.lang.functional.curry (được ghi trong Functional fun in JavaScript with Dojo — chỉ cần xem trên trang này để "cà ri"). Cụ thể, bạn có thể muốn xem curry() và partial().

curry() tích lũy các đối số (như trong ví dụ của bạn) nhưng với một sự khác biệt: ngay sau khi arity hài lòng, nó gọi hàm trả về giá trị. Triển khai ví dụ của bạn:

df.curry(out)("hello")("world"); 
df.curry(out)("hello", "world"); 

Lưu ý rằng dòng cuối cùng không có "()" ở cuối — nó được gọi tự động.

phần() cho phép để thay thế lập luận một cách ngẫu nhiên:

df.partial(out, df.arg, "world")("hello"); 
0

** EDIT: Xem phản ứng Jason Bunting của. Câu trả lời này thực sự cho thấy một cách ngang hàng loạt các chuỗi cuộc gọi ra, không phải là một cuộc gọi out-out duy nhất với các cài đặt trước cho một số đối số. Nếu câu trả lời này thực sự giúp với một vấn đề tương tự, bạn nên chắc chắn sử dụng áp dụng và gọi như Jason đề xuất, thay vì cách tối nghĩa để sử dụng eval mà tôi nghĩ ra. **

Vâng ... ra của bạn sẽ thực sự viết "không xác định" rất nhiều trong việc này ... nhưng điều này nên được gần gũi với những gì bạn muốn:

function out(a, b) { 
    document.write(a + " " + b); 
} 

function getArgString(args, start) { 
    var argStr = ""; 
    for(var i = start; i < args.length; i++) { 
     if(argStr != "") { 
      argStr = argStr + ", "; 
     } 
     argStr = argStr + "arguments[" + i + "]" 
    } 
    return argStr; 
} 

function setter(func) { 
    var argStr = getArgString(arguments, 1); 
    eval("func(" + argStr + ");"); 
    var newSettter = function() { 
     var argStr = getArgString(arguments, 0); 
     if(argStr == "") { 
      argStr = "func"; 
     } else { 
      argStr = "func, " + argStr; 
     } 
     return eval("setter(" + argStr + ");"); 
    } 
    return newSettter; 
} 

setter(out, "hello")("world"); 
setter(out, "hello", "world")(); 

tôi có lẽ muốn di chuyển mã trong getArgString vào chức năng setter chính nó mặc dù ... một chút an toàn hơn kể từ khi tôi sử dụng 'eval's.

+0

Ý tưởng đúng, nhưng thực thi khủng khiếp. Xem câu trả lời của tôi - bạn không cần phải xây dựng mọi thứ dưới dạng chuỗi, JavaScript có các hàm call() và apply() làm cho những thứ như thế này dễ dàng hơn. –

+0

Huh ... không bao giờ nghe nói về cuộc gọi hoặc áp dụng các chức năng trước đây - những người làm cho nó dễ dàng hơn. – Illandril

+0

Tôi khuyên bạn nên dành một chút thời gian để đọc chúng - trong khi chúng có một cách sử dụng rất thích hợp, chúng khá hữu ích cho những mục đích sử dụng đó! –

0

Bạn có thể sử dụng Function.prototype.bind() cho việc này. Đó là một bổ sung ES5.

Ngoài cách sử dụng chung của bối cảnh chức năng thiết lập (this giá trị), nó cũng có thể đặt đối số một phần.

function out(a, b) { 
 
    document.write(a + " " + b); 
 
} 
 

 
function setter(func) { 
 
    return func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1))); 
 
} 
 

 
setter(out, "hello")("world"); 
 
setter(out, "hello", "world")();

chức năng My setter là thực sự rất đơn giản. Phần dài nhất là chỉ nhận được danh sách các đối số. Tôi sẽ chia nhỏ mã như sau:

func.bind.apply(func, [window].concat([].slice.call(arguments).slice(1))) 
func.bind.apply(              ) // need to use apply to pass multiple arguments as an array to bind() 
       func,              // apply needs a context to be run in 
         [window].concat(        ) // pass an array of arguments to bind(), starting with window, to be the global context 
             [].slice.call(arguments).slice(1) // convert the arguments list to an array, and chop off the initial value 

Được hỗ trợ trong these browsers: Chrome 7+, Firefox 4+, IE9 +. MDN (được liên kết ngay từ đầu) có một polyfill.

0
function myLog(arg1,arg2,arg3){ 
    return function(){ 
     //you can use the arguments from the parent scope 
     console.log(arg1+" "+arg2+" "+arg3); 
    }; 
} 
var passMeAnywhere = myLog(1,2,3); 
//execute the function, args will be in the immediate lexical scope. 
passMeAnywhere(); 
//One of the previous posts showed a way for context binding 
//Context binding allows any context 
//New is not required with binding 
+0

không hoạt động. bạn đã thử cái này chưa? –

1

Sử dụng Javascript của apply(), bạn có thể thay đổi function prototype

Function.prototype.pass = function() { 
    var args = arguments, 
     func = this; 
    return function() { 
     func.apply(this, args); 
    } 
}; 

Sau đó bạn có thể gọi nó như out.pass('hello','world')

apply mất một mảng cho 2 lập luận/tham số.

arguments là thuộc tính có sẵn bên trong hàm chứa tất cả tham số trong mảng giống như cấu trúc.

Một cách phổ biến khác để làm điều này là sử dụng bind

loadedFunc = func.bind(this, v1, v2, v3);

sau đó

loadedFunc() === this.func(v1,v2,v3);

này kinda đủ, mặc dù ít xấu xí.

+1

tôi nghĩ rằng bạn nên thêm một lời giải thích thích hợp cách thức hoạt động này; Một ví dụ về chức năng của OP nên được bao gồm quá. – sintakonte

+1

Rất nhiều người chống lại các sửa đổi của nguyên mẫu cơ sở cho các mục đích tầm thường, nhưng điều này có vẻ lý tưởng, duy trì khả năng đọc trong suốt mã của bạn. –

+0

cá nhân tôi nghĩ rằng tôi đã chỉ được sử dụng liên kết cho hầu hết các trường hợp mặc dù, bệnh thêm một ví dụ về cách –

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