Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling optional parameters in javascript

Tags:

javascript

People also ask

How do you pass optional arguments in JavaScript?

Using the Logical OR operator ('||') In this method, the optional parameter is "Logically ORed" with the default value within the body of the function. In the example below, if the value of b is undefined, 2 is passed instead.

Are parameters always optional in JavaScript?

In JavaScript, function parameters default to undefined . However, it's often useful to set a different default value. This is where default parameters can help.

How do I set optional parameters?

Using Optional Attribute Here for the [Optional] attribute is used to specify the optional parameter. Also, it should be noted that optional parameters should always be specified at the end of the parameters. For ex − OptionalMethodWithDefaultValue(int value1 = 5, int value2) will throw exception.

How do you pass optional parameters in TypeScript?

In Typescript, making optional parameters is done by appending the “?” at the end of the parameter name in the function when declaring the parameters and the parameters which are not marked with “?” i.e not optional parameter are called as default parameters or normal parameters where it is must and compulsory to pass ...


You can know how many arguments were passed to your function and you can check if your second argument is a function or not:

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

You can also use the arguments object in this way:

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

If you are interested, give a look to this article by John Resig, about a technique to simulate method overloading on JavaScript.


Er - that would imply that you are invoking your function with arguments which aren't in the proper order... which I would not recommend.

I would recommend instead feeding an object to your function like so:

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

Benefits:

  • you don't have to account for argument order
  • you don't have to do type checking
  • it's easier to define default values because no type checking is necessary
  • less headaches. imagine if you added a fourth argument, you'd have to update your type checking every single time, and what if the fourth or consecutive are also functions?

Drawbacks:

  • time to refactor code

If you have no choice, you could use a function to detect whether an object is indeed a function ( see last example ).

Note: This is the proper way to detect a function:

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

isFunction( function(){} )

You should check type of received parameters. Maybe you should use arguments array since second parameter can sometimes be 'parameters' and sometimes 'callback' and naming it parameters might be misleading.


I know this is a pretty old question, but I dealt with this recently. Let me know what you think of this solution.

I created a utility that lets me strongly type arguments and let them be optional. You basically wrap your function in a proxy. If you skip an argument, it's undefined. It may get quirky if you have multiple optional arguments with the same type right next to each other. (There are options to pass functions instead of types to do custom argument checks, as well as specifying default values for each parameter.)

This is what the implementation looks like:

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

For clarity, here is what is going on:

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

           }
         );
};

You can view the arrangeArgs proxy code in my GitHub repository here:

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

Here is the utility function with some comments copied from the repository:

/*
 ****** 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();
}

I recommend you to use ArgueJS.

You can just type your function this way:

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

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

I considered by your examples that you want your id parameter to be a string, right? Now, getData is requiring a String id and is accepting the optionals Object parameters and Function callback. All the use cases you posted will work as expected.