Xử lý các tham số tùy chọn trong javascript


127

Tôi có một hàm javascript tĩnh có thể lấy 1, 2 hoặc 3 tham số:

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ố đã cho không được xác định, nhưng làm thế nào để tôi biết nếu thông số được truyền là tham số hay gọi lại?

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


Ví dụ về những gì có thể được thông qua:

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);

2
Bạn có thể đưa ra một ví dụ về những gì có thể được thông qua?
James Black

Câu trả lời:


163

Bạn có thể biết có bao nhiêu đối số được truyền cho hàm của mình và bạn có thể kiểm tra xem đối số thứ hai của mình có phải là hàm 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 đối tượng đối số 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 bài viết này của John Resig, về một kỹ thuật mô phỏng quá tải phương thức trên JavaScript.


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

@TomerCagan Tôi nghĩ đó là vấn đề ưu tiên (ish). Có một số câu trả lời / nhận xét tốt về chủ đề dưới câu hỏi này .
Philiiiiiipp

75

Er - điều đó có nghĩa là bạn đang gọi hàm của mình bằng 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ì cho một đối tượng vào chức năng của bạn như vậy:

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'); }
} );

Những lợi ích:

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

Hạn chế:

  • thời gian để mã cấu trúc lại

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

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

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

isFunction( function(){} )

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

Tôi đã thêm mã chính xác để phát hiện một chức năng. Tôi tin rằng lỗi ở đây: bug.ecmascript.org/ticket/251
meder omuraliev

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.
meder omuraliev

Ôi ... chết tiệt ... Chỉ cần đăng cùng một ý tưởng.
Arnis Lapsa

1
Một nhược điểm khác có thể là thiếu intellisense. Tôi không coi đây là một vấn đề lớn, nhưng cần lưu ý.
Edyn


2

Bạn nên kiểm tra loại tham số nhận được. Có lẽ bạn nên sử dụng argumentsmảng 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.


2

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

Tôi đã tạo một tiện ích cho phép tôi gõ mạnh các đối số và để chúng là tùy chọn. Về cơ bản 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ố, nó 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ó cùng loại ngay cạnh nhau. (Có các tùy chọn để truyền các hàm thay vì các loại để kiểm tra đối số tùy chỉnh, cũng như chỉ định các giá trị mặc định cho từng tham số.)

Đây là những gì việc thực hiện trô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 mã proxy sortArss trong kho GitHub của tôi tại đây:

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

Đây là chức năng tiện ích với một số ý kiến ​​đượ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();
}

2

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 các ví dụ của bạn rằng bạn muốn idtham số của bạn là một chuỗi, phải không? Bây giờ, getDatađang yêu cầu một String idvà đ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ẽ làm việc như mong đợi.



1

Bạn có nói rằng bạn có thể có các cuộc gọi như sau: getData (id, tham số); getData (id, gọi lại)?

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

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


0

Tôi nghĩ bạn muốn sử dụng typeof () tại đâ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"

0

Nếu vấn đề của bạn chỉ là do quá tải chức năng (bạn cần kiểm tra xem 'tham số' là 'tham số' chứ không phải 'gọi lại'), tôi khuyên bạn không nên bận tâm về loại đối số và
sử dụng phương pháp này . Ý tưởng rất đơn giản - sử dụng các đối tượng bằng chữ để kết hợp các tham 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!");}
});

0

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

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);
}

-5

Bạn có thể ghi đè chức năng? Điều này sẽ không hoạt động:

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

8
Không, điều này sẽ không hoạt động. Bạn sẽ không gặp phải bất kỳ lỗi nào nhưng Javascript sẽ luôn sử dụng chức năng mới nhất mà bạn đã xác định.
jd.

6
Ồ Tôi nghĩ bạn bị điên Tôi chỉ thử nó. Bạn đúng rồi. Thế giới của tôi chỉ thay đổi một chút cho 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ứ thứ gì như thế này trong sản xuất. Cảm ơn các bình luận. Tôi đã cho bạn +1.
J.Hendrix
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.