2009-10-07 61 views
124

Tôi có một hàm javascript tĩnh có thể kéo dài từ 1, 2 hoặc 3 thông số:Xử lý các thông số tùy chọn trong javascript

function getData(id, parameters, callback) //parameters (associative array) and callback (function) are optional 

Tôi biết tôi luôn có thể kiểm tra nếu một tham số đưa ra là không xác định, nhưng làm thế nào tôi có thể biết nếu những gì đã được thông qua là tham số hoặc gọi lại?

Cách tốt nhất để làm điều này là gì?


Các ví dụ về những gì có thể được thông qua tại:

1:

getData('offers'); 

2:

var array = new Array(); 
array['type']='lalal'; 
getData('offers',array); 

3:

var foo = function(){...} 
getData('offers',foo); 

4:

getData('offers',array,foo); 
+1

Bạn có thể hiển thị một ví dụ về những gì có thể được thông qua trong? –

Trả lời

160

Bạn có thể biết có bao nhiêu arguments được truyền cho chức năng của bạn và bạn có thể kiểm tra nếu đối số thứ hai của bạn là một chức năng hay không:

function getData (id, parameters, callback) { 
    if (arguments.length == 2) { // if only two arguments were supplied 
    if (Object.prototype.toString.call(parameters) == "[object Function]") { 
     callback = parameters; 
    } 
    } 
    //... 
} 

Bạn cũng có thể sử dụng các đối số đối tượng theo cách này:

function getData (/*id, parameters, callback*/) { 
    var id = arguments[0], parameters, callback; 

    if (arguments.length == 2) { // only two arguments supplied 
    if (Object.prototype.toString.call(arguments[1]) == "[object Function]") { 
     callback = arguments[1]; // if is a function, set as 'callback' 
    } else { 
     parameters = arguments[1]; // if not a function, set as 'parameters' 
    } 
    } else if (arguments.length == 3) { // three arguments supplied 
     parameters = arguments[1]; 
     callback = arguments[2]; 
    } 
    //... 
} 

Nếu bạn quan tâm, hãy xem xét điều này article bởi John Resig, về kỹ thuật mô phỏng quá tải phương thức trên JavaScript.

+0

Tại sao nên sử dụng Object.prototype.toString.call (tham số) == "[đối tượng Hàm]" và không phải typeof (tham số) === 'chức năng'? Có một số khác biệt quan trọng giữa chúng? P.S. bài viết bạn đề cập dường như sử dụng sau này –

+0

@TomerCagan Tôi nghĩ rằng đó là một vấn đề ưu tiên (ish). Có một số câu trả lời hay/nhận xét tốt về chủ đề trong câu hỏi này [http://stackoverflow.com/q/5999998/690912). – Philiiiiiipp

72

Er - điều đó có nghĩa là bạn đang gọi hàm của bạn với các đối số không theo đúng thứ tự ... mà tôi không khuyến nghị.

Tôi muốn giới thiệu thay vì ăn một đối tượng chức năng của bạn như sau:

function getData(props) { 
    props = props || {}; 
    props.params = props.params || {}; 
    props.id = props.id || 1; 
    props.callback = props.callback || function(){}; 
    alert(props.callback) 
}; 

getData({ 
    id: 3, 
    callback: function(){ alert('hi'); } 
}); 

Lợi ích:

  • bạn không cần phải giải thích cho trật tự luận
  • bạn không có để kiểm tra loại
  • việc xác định giá trị mặc định dễ dàng hơn vì không cần kiểm tra loại
  • ít đau đầu hơn. hãy tưởng tượng nếu bạn thêm một đối số thứ tư, bạn phải cập nhật loại của bạn kiểm tra mỗi lần duy nhất, và điều gì sẽ xảy ra nếu thứ tư hoặc liên tiếp cũng có chức năng?

Nhược điểm:

  • thời gian để refactor code

Nếu bạn không có sự lựa chọn, bạn có thể sử dụng một chức năng để phát hiện xem một đối tượng thực sự là một chức năng (xem ví dụ cuối cùng).

Lưu ý: Đây là cách thích hợp để phát hiện một chức năng:

function isFunction(obj) { 
    return Object.prototype.toString.call(obj) === "[object Function]"; 
} 

isFunction(function(){}) 
+0

"điều này sẽ làm việc 99% thời gian do một số lỗi ES." Bạn có thể giải thích thêm không? Tại sao nó có thể đi sai? –

+0

Tôi đã thêm mã đúng để phát hiện một hàm. Tôi tin rằng lỗi là ở đây: http://bugs.ecmascript.org/ticket/251 –

+0

Tôi rất muốn đề nghị bạn chỉ ăn một đối tượng. Nếu không, hãy sử dụng phương pháp của CMS. –

2

Vì vậy, sử dụng toán tử typeof để xác định nếu tham số thứ hai là một mảng hoặc chức năng.

này có thể đưa ra một số gợi ý: http://www.planetpdf.com/developer/article.asp?ContentID=testing_for_object_types_in_ja

Tôi không chắc chắn nếu đây là công việc hay bài tập ở nhà, vì vậy tôi không muốn cung cấp cho bạn câu trả lời vào lúc này, nhưng typeof sẽ giúp bạn xác định nó .

2

Bạn nên kiểm tra loại tham số nhận được. Có lẽ bạn nên sử dụng mảng arguments vì tham số thứ hai đôi khi có thể là 'tham số' và đôi khi 'gọi lại' và đặt tên tham số có thể gây hiểu nhầm.

0

Tôi nghĩ rằng bạn muốn sử dụng typeof() ở đây:

function f(id, parameters, callback) { 
    console.log(typeof(parameters)+" "+typeof(callback)); 
} 

f("hi", {"a":"boo"}, f); //prints "object function" 
f("hi", f, {"a":"boo"}); //prints "function object" 
1

Bạn đang nói rằng bạn có thể có cuộc gọi như thế này: getData (id, các thông số); getData (id, gọi lại)?

Trong trường hợp này bạn có thể không rõ ràng dựa vào vị trí và bạn phải dựa vào phân tích các loại: getType() và sau đó nếu cần thiết getTypeName()

Kiểm tra xem các tham số trong câu hỏi là một mảng hoặc một hàm.

-5

Bạn có thể ghi đè hàm này không? Liệu điều này không làm việc:

function doSomething(id){} 
function doSomething(id,parameters){} 
function doSomething(id,parameters,callback){} 
+8

Không, thao tác này sẽ không hoạt động. Bạn sẽ không nhận được bất kỳ lỗi nào nhưng Javascript sẽ luôn sử dụng hàm mới nhất mà bạn đã xác định. –

+6

Wow. Tôi nghĩ bạn bị điên. Tôi vừa thử nghiệm nó. Bạn đúng rồi. Thế giới của tôi chỉ thay đổi một chút đối với tôi. Tôi nghĩ rằng tôi có rất nhiều JavaScript để xem xét ngày hôm nay để đảm bảo rằng tôi không có bất cứ điều gì như thế này trong sản xuất. Cảm ơn bạn đã bình luận. Tôi đã cho bạn một +1. –

0

Nếu vấn đề của bạn là chỉ có chức năng quá tải (bạn cần phải kiểm tra xem thông số 'thông số' là 'thông số' và không 'callback'), tôi sẽ khuyên bạn đừng bận tâm về loại đối số và
sử dụng this approach. Ý tưởng rất đơn giản - sử dụng các đối tượng theo nghĩa đen để kết hợp các thông số của bạn:

function getData(id, opt){ 
    var data = voodooMagic(id, opt.parameters); 
    if (opt.callback!=undefined) 
     opt.callback.call(data); 
    return data;   
} 

getData(5, {parameters: "1,2,3", callback: 
    function(){for (i=0;i<=1;i--)alert("FAIL!");} 
}); 
2

Tôi biết đây là một câu hỏi khá cũ, nhưng tôi đã xử lý vấn đề này gần đây. Hãy cho tôi biết bạn nghĩ gì về giải pháp này.

Tôi đã tạo một tiện ích cho phép tôi nhập mạnh mẽ các đối số và cho phép chúng là tùy chọn. Bạn về cơ bản bọc chức năng của bạn trong một proxy. Nếu bạn bỏ qua một đối số, đó là không xác định. Nó có thể trở nên kỳ quặc nếu bạn có nhiều đối số tùy chọn cùng loại ngay bên cạnh nhau. (Có các tùy chọn để truyền các hàm thay vì các kiểu để thực hiện kiểm tra đối số tùy chỉnh, cũng như chỉ định các giá trị mặc định cho mỗi tham số.)

Đây là những gì thực hiện trông giống như:

function displayOverlay(/*message, timeout, callback*/) { 
    return arrangeArgs(arguments, String, Number, Function, 
    function(message, timeout, callback) { 
     /* ... your code ... */ 
    }); 
}; 

Để rõ ràng, đây là những gì đang xảy ra:

function displayOverlay(/*message, timeout, callback*/) { 
    //arrangeArgs is the proxy 
    return arrangeArgs(
      //first pass in the original arguments 
      arguments, 
      //then pass in the type for each argument 
      String, Number, Function, 
      //lastly, pass in your function and the proxy will do the rest! 
      function(message, timeout, callback) { 

      //debug output of each argument to verify it's working 
      console.log("message", message, "timeout", timeout, "callback", callback); 

      /* ... your code ... */ 

      } 
     ); 
}; 

Bạn có thể xem arrangeArgs mã proxy trong kho GitHub của tôi ở đây:

https://github.com/joelvh/Sysmo.js/blob/master/sysmo.js

Đây là tiện ích fu nction với một số nhận xét được sao chép từ kho lưu trữ:

/* 
****** Overview ****** 
* 
* Strongly type a function's arguments to allow for any arguments to be optional. 
* 
* Other resources: 
* http://ejohn.org/blog/javascript-method-overloading/ 
* 
****** Example implementation ****** 
* 
* //all args are optional... will display overlay with default settings 
* var displayOverlay = function() { 
* return Sysmo.optionalArgs(arguments, 
*   String, [Number, false, 0], Function, 
*   function(message, timeout, callback) { 
*    var overlay = new Overlay(message); 
*    overlay.timeout = timeout; 
*    overlay.display({onDisplayed: callback}); 
*   }); 
* } 
* 
****** Example function call ****** 
* 
* //the window.alert() function is the callback, message and timeout are not defined. 
* displayOverlay(alert); 
* 
* //displays the overlay after 500 miliseconds, then alerts... message is not defined. 
* displayOverlay(500, alert); 
* 
****** Setup ****** 
* 
* arguments = the original arguments to the function defined in your javascript API. 
* config = describe the argument type 
* - Class - specify the type (e.g. String, Number, Function, Array) 
* - [Class/function, boolean, default] - pass an array where the first value is a class or a function... 
*           The "boolean" indicates if the first value should be treated as a function. 
*           The "default" is an optional default value to use instead of undefined. 
* 
*/ 
arrangeArgs: function (/* arguments, config1 [, config2] , callback */) { 
    //config format: [String, false, ''], [Number, false, 0], [Function, false, function(){}] 
    //config doesn't need a default value. 
    //config can also be classes instead of an array if not required and no default value. 

    var configs = Sysmo.makeArray(arguments), 
     values = Sysmo.makeArray(configs.shift()), 
     callback = configs.pop(), 
     args = [], 
     done = function() { 
     //add the proper number of arguments before adding remaining values 
     if (!args.length) { 
      args = Array(configs.length); 
     } 
     //fire callback with args and remaining values concatenated 
     return callback.apply(null, args.concat(values)); 
     }; 

    //if there are not values to process, just fire callback 
    if (!values.length) { 
    return done(); 
    } 

    //loop through configs to create more easily readable objects 
    for (var i = 0; i < configs.length; i++) { 

    var config = configs[i]; 

    //make sure there's a value 
    if (values.length) { 

     //type or validator function 
     var fn = config[0] || config, 
      //if config[1] is true, use fn as validator, 
      //otherwise create a validator from a closure to preserve fn for later use 
      validate = (config[1]) ? fn : function(value) { 
      return value.constructor === fn; 
      }; 

     //see if arg value matches config 
     if (validate(values[0])) { 
     args.push(values.shift()); 
     continue; 
     } 
    } 

    //add a default value if there is no value in the original args 
    //or if the type didn't match 
    args.push(config[2]); 
    } 

    return done(); 
} 
1

Tôi khuyên bạn nên sử dụng ArgueJS.

Bạn chỉ có thể gõ chức năng của bạn theo cách này:

function getData(){ 
    arguments = __({id: String, parameters: [Object], callback: [Function]}) 

    // and now access your arguments by arguments.id, 
    //   arguments.parameters and arguments.callback 
} 

tôi xem xét ví dụ của bạn mà bạn muốn tham số id của bạn trở thành một chuỗi, phải không? Hiện tại, getData yêu cầu String id và đang chấp nhận các tùy chọn Object parametersFunction callback. Tất cả các trường hợp sử dụng bạn đã đăng sẽ hoạt động như mong đợi.

0

này tôi đoán có thể tự giải thích ví dụ:

function clickOn(elem /*bubble, cancelable*/) { 
    var bubble =  (arguments.length > 1) ? arguments[1] : true; 
    var cancelable = (arguments.length == 3) ? arguments[2] : true; 

    var cle = document.createEvent("MouseEvent"); 
    cle.initEvent("click", bubble, cancelable); 
    elem.dispatchEvent(cle); 
} 
Các vấn đề liên quan