1
\$\begingroup\$

I'm writing a function that takes between one and three arguments (function callback [, number limit] [, regexp pattern]). Since the second two are both optional, I need to determine which argument is being passed when the function is supplied with only two. Below is my solution, but it seems overly verbose. Is there a cleaner way to do this?

  var get_queued_pages = function() {
    if(arguments>0){
        var callback = arguments[0]; 
        if(arguments.length == 3){ //all arguments are being passed     
        var limit    = arguments[1];
        var pattern  = arguments[2];
        }
        else if(arguments.length == 2){ //only 2 arguments passed
        if(typeof(arguments[1]) == "number"){ //check if the second one is limit or pattern             
            var limit = arguments[1];//no pattern defined; second argument is limit
        }
        }
        else{
        var limit = 1;
        }
    }
    //do stuff

}
\$\endgroup\$
  • \$\begingroup\$ Can the order of which they are passed in change? Ie. if there is a second one, will it always be a number etc. Or could the order change possibly? Being were the regexp gets passed in first or vice versa. \$\endgroup\$ – Jonny Sooter Mar 13 '13 at 14:33
  • \$\begingroup\$ In this example, no. If there is a way to elegantly handle that too it would be extra nice though! \$\endgroup\$ – devnill Mar 13 '13 at 14:42
3
\$\begingroup\$
function(cb,limit,pattern) {
    limit = typeof(limit) === "undefined" ? 1 : limit;
    //do stuff
}

Calling a function with less arguments than declared will just fill them with undefined. You can then check if that is the value, if so fill in your default ones.
The I have above will have the same affect as yours, there are always three variables (even in your code, Javascript variables are function scope, all of the var declarations are pulled to the top.).
The variables:
cb - unchanged
limit - set to 1 if not supplied pattern - unchanged (undefined if not supplied)

EDIT: I just saw your comment on reordering you can do the following

  function foo(cb,limit,pattern) {
        if(limit instanceof RegExp) {
            pattern = limit;
            limit = 1;
        }
        else {
            limit = typeof(limit) === "undefined" ? 1 : limit;
        }
        //do stuff
    }
\$\endgroup\$
2
\$\begingroup\$

One thing I did notice in your code is that you're testing if arguments > 0. I assume you mean arguments.length > 0.

Anyway, for simple stuff like this, you don't really need to use arguments.length.

In JavaScript, if you have a function that takes, say three arguments:

function doSomething(callback, limit, pattern) {
   // do something
}

You can still call it passing one argument:

doSomething(foo);

What will happen is that inside the function body, the first parameter will be set and other parameters will be undefined. The easiest/shortest way to implement what you want is simply to check if limit and pattern are not undefined:

if (typeof(limit) != 'undefined') { ... }

arguments is better suited for more complicated variadic functions (think of printf in C, for example, or console.log in JavaScript) that may take arbitrary numbers of arguments.

\$\endgroup\$
  • \$\begingroup\$ You are correct, I did forget length. I see what you are saying in your example, but what if only the first and third arg were passed? Is it a better practice to leave the argument names ambiguous so pattern isn't defined as limit? \$\endgroup\$ – devnill Mar 13 '13 at 18:48
2
\$\begingroup\$

I think that you shouldn't be testing for undefined arguments in order to change your function behavior, you should be aware that someone can call the function with 3 arguments from previous processing, but the content of one of them could be undefined. In that case the best would be testing arguments.length before validating argument contents.

Seems to me a better practice to declare and respect each function signature, for example:

function Operation() {
  this.setLimit('default limit here');
  this.setPattern(/default pattern here/);
}

Operation.prototype.limit = function(limit) {
  // validate limit here
  this.limit = limit;
  return this;
};

Operation.prototype.pattern = function(pattern) {
  // validate pattern here
  this.pattern = pattern;
  return this;
};

Operation.prototype.execute = function(callback) {
  // operation here, then call `callback`
};

var op = new Operation();
op.limit(142)
  .pattern(/^.*$/i)
  .execute(function() {});

// you can set or modify all parameters as you wish, in any order, each with their proper signature, and execute it when ready.

or use a options hash, for optional params:

function realizeOperation(callback, options) {
    var limit   = options && options.limit   || 'default limit here';
    var pattern = options && options.pattern || /default pattern here/;

    // option validations here
    // operation here
}

There are also plenty of libs for handling and validating parameters, if you really want to do it: https://www.npmjs.com/browse/keyword/parameters

\$\endgroup\$
0
\$\begingroup\$

What about passing an object?

var foo = function(param) {
    var p1 = param.p1 || null;
    var p2 = param.p2 || 'default';
    var p3 = param.p3 || {};

    // ....
}

foo({p3: 3.14, p1: function() {...}});

I've found this to be elegant, simple and easy to extend.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.