Обработка необязательных параметров в JavaScript

javascript

114220 просмотра

12 ответа

У меня есть статическая функция JavaScript, которая может принимать 1, 2 или 3 параметра:

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

Я знаю, что всегда могу проверить, является ли данный параметр неопределенным, но как мне узнать, был ли передан параметр или обратный вызов?

Какой лучший способ сделать это?


Примеры того, что можно передать в:

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);
Автор: jd. Источник Размещён: 12.11.2019 09:08

Ответы (12)


163 плюса

Решение

Вы можете знать, сколько аргументов было передано вашей функции, и вы можете проверить, является ли ваш второй аргумент функцией или нет:

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; 
    }
  }
  //...
}

Вы также можете использовать объект arguments таким образом:

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];
  }
  //...
}

Если вам интересно, ознакомьтесь с этой статьей Джона Резига о методике имитации перегрузки методов в JavaScript.

Автор: CMS Размещён: 07.10.2009 01:53

75 плюса

Э-э - это будет означать, что вы вызываете свою функцию с аргументами, которые не в правильном порядке ... что я бы не рекомендовал.

Я бы порекомендовал вместо этого подавать объект вашей функции следующим образом:

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

Выгоды:

  • вам не нужно учитывать порядок аргументов
  • вам не нужно делать проверку типов
  • проще определить значения по умолчанию, потому что проверка типов не требуется
  • меньше головных болей представьте, если вы добавите четвертый аргумент, вам придется обновлять проверку типов каждый раз, а что, если четвертый или последовательный являются также функциями?

Недостатки:

  • время для рефакторинга кода

Если у вас нет выбора, вы можете использовать функцию, чтобы определить, действительно ли объект является функцией (см. Последний пример).

Примечание. Это правильный способ определения функции:

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

isFunction( function(){} )
Автор: meder omuraliev Размещён: 07.10.2009 01:48

2 плюса

Поэтому используйте оператор typeof, чтобы определить, является ли второй параметр массивом или функцией.

Это может дать некоторые предложения: http://www.planetpdf.com/developer/article.asp?ContentID=testing_for_object_types_in_ja

Я не уверен, что это работа или домашняя работа, поэтому я не хочу сейчас давать вам ответ, но typeof поможет вам определить это.

Автор: James Black Размещён: 07.10.2009 01:48

2 плюса

Вам следует проверить тип полученных параметров. Возможно, вам следует использовать argumentsмассив, так как второй параметр может иногда быть «параметрами», а иногда «обратным вызовом», и присвоение ему имен параметров может вводить в заблуждение.

Автор: Kamil Szot Размещён: 07.10.2009 01:49

2 плюса

Я знаю, что это довольно старый вопрос, но я занимался этим недавно. Дайте мне знать, что вы думаете об этом решении.

Я создал утилиту, которая позволяет мне строго вводить аргументы и позволять им быть необязательными. Вы в основном заключаете свою функцию в прокси. Если вы пропустите аргумент, он не определен . Это может показаться странным, если у вас есть несколько необязательных аргументов с одинаковым типом рядом друг с другом. (Существуют варианты передачи функций вместо типов для выполнения пользовательских проверок аргументов, а также указания значений по умолчанию для каждого параметра.)

Вот как выглядит реализация:

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

Для ясности, вот что происходит:

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 ... */

           }
         );
};

Вы можете просмотреть прокси-кодrangeArgs в моем репозитории GitHub здесь:

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

Вот полезная функция с некоторыми комментариями, скопированными из хранилища:

/*
 ****** 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();
}
Автор: joelvh Размещён: 08.12.2011 03:41

2 плюса

Я рекомендую вам использовать ArgueJS .

Вы можете просто напечатать свою функцию следующим образом:

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

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

Я рассмотрел ваши примеры, что вы хотите, чтобы ваш idпараметр был строкой, верно? Теперь getDataтребуется String idи принимает дополнительные Object parametersи Function callback. Все опубликованные вами сценарии использования будут работать как положено.

Автор: zVictor Размещён: 12.04.2013 03:17

1 плюс

Вы можете использовать свойство объекта arguments внутри функции.

Автор: Ryu Размещён: 07.10.2009 01:47

1 плюс

Вы говорите, что у вас могут быть такие звонки: getData (id, параметры); getData (идентификатор, обратный вызов)?

В этом случае вы, очевидно, не можете полагаться на позицию, и вы должны полагаться на анализ типа: getType () и затем, если необходимо, getTypeName ()

Проверьте, является ли рассматриваемый параметр массивом или функцией.

Автор: DmitryK Размещён: 07.10.2009 01:51

0 плюса

Я думаю, что вы хотите использовать typeof () здесь:

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"
Автор: Mark Bessey Размещён: 07.10.2009 01:50

0 плюса

Если ваша проблема связана только с перегрузкой функции (вам необходимо проверить, является ли параметр «параметры» параметром «параметры», а не «обратный вызов»), я бы порекомендовал вам не беспокоиться о типе аргумента и
использовать этот подход . Идея проста - использовать буквальные объекты для объединения ваших параметров:

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!");}
});
Автор: Arnis Lapsa Размещён: 07.10.2009 10:23

0 плюса

Это, я думаю, может быть очевидным примером:

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);
}
Автор: ses Размещён: 22.04.2015 07:04

-5 плюса

Можете ли вы переопределить функцию? Это не будет работать:

function doSomething(id){}
function doSomething(id,parameters){}
function doSomething(id,parameters,callback){}
Автор: J.Hendrix Размещён: 07.10.2009 01:54
Вопросы из категории :
32x32