views:

540

answers:

8

So I admit that I'm new to javascript and that I come from a C.+ background ("Hi, I'm Bob, I'm a class-based static language user", chorus "hi Bob!").

I find that I often end up writing functions like:

function someFunc()
{
    if (arguments.length === 0 ){
       ...
    } else {
       ...
    }
}

(where there might be three such cases). Or, alternatively, I write the difference into the name:

function someFuncDefault() { ... };
function someFuncRealArg(theArg) { ... };

(Substitute "RealArg" for some semantically contentful phrase).

Is there a better pattern for this kind of thing?

+2  A: 

I don't know that I would do it this way, but it seems like it might make your code mildly less unmanageable:

function someFunc() {
    switch (arguments.length) {
        case 0: noArgs();
        case 1: oneArg(arguments[0]);
        case 2: twoArgs(arguments[0], arguments[1]);
    }
    function noArgs() {
        // ...
    }
    function oneArg(a) {
        // ...
    }
    function twoArgs(a, b) {
        // ...
    }
}

Another example might be:

function someFunc(a, b) {
    if ('string' == typeof a) {
        // ...
    } else if ('number' == typeof a) {
        // ...
    }
}

And of course you can probably create something quite unmanageable by combining both examples (using conditions to determine behaviour based on number of arguments and types of arguments).

Grant Wagner
I never knew about the argument array before. That's fantastic!
notnot
I don't see how values returned from the nested functions in your example are being handled
George Jempty
I don't like this... it doesn't really get around his issue of clutter in the code base, it just gives him a different way to have more methods with funny names.
Akrikos
@George: If you want to handle the return values, simply substitute case 0: return oneArg(arguments[0]); for what I have now.
Grant Wagner
@Akrikos: The methods with funny names are encapsulated within the defining functions and do not clutter the global namespace. If you are concerned about having a noArgs() function in every function designed this way, prefix them... someFunc_noArgs().
Grant Wagner
@Akrikos: An alternative is to include the code that handles each 'case' statement inline and not have other functions at all. I was trying to make the code neater by doing case 1: someFunc_oneArg(arguments[0]); instead of case 1: /* potentially lots and lots and lots of inline code here */;
Grant Wagner
+4  A: 

Have a look at this post.

jcrossley3
Neat! Not something I'd recommend for a beginner (if something went wrong...) but very cool!
Akrikos
After looking at his question, I think this is the best option for what he wants to do. Otherwise he might try attaching methods to objects, but I don't think that'll help him in this case.
Akrikos
+1  A: 

In Javascript, all arguments are optional.

You might try something like:

Edit (better method that doesn't break for values whose 'truthiness' is false):

function bar(arg1, arg2) {
  if(arg1 === undefined) { set default }
  if(arg2 === undefined) { set default }
  //do stuff here
}

Old method that breaks for falsey values:

function foo(arg1, arg2) {
  if(!arg1) { set default }
  if(!arg2) { set default }
  //do stuff here
}

A great place to start with javascript are Douglas Crockford's javascript lectures: http://video.yahoo.com/search/?p=javascript

Akrikos
One potential downfall to this approach is that you might want to be able to pass something that evaluates to false (such as 0) to your function. The typeof function might be safer.
Jeremy DeGroot
true... I thought of putting that caveat, but forgot when writing :-(
Akrikos
hmm... it looks like I misread the question. He's already doing something similar to this.
Akrikos
A: 

Javascript lets you get really lazy with this (not quite as lazy as Python, but pretty lazy).

function someFunc(arg1, arg2)
{
  if(typeof(arg1) == "undefined") {
    arg1 = default;
  }
  ... 
}

So you don't really need to overload. Javascript won't yell at you for passing the wrong number of parameters.

Jeremy DeGroot
Please fix misspelling of undefined
Akrikos
Is there a reason you check for 'typeof' rather than just using === the javascript reserved word undefined?
Akrikos
My gut tells me it's because some browser that screwed up Javascript didn't have undefined defined as a reserved word, but I don't know that for sure.
Jeremy DeGroot
+1  A: 

This is overloading, not overriding no?

Javascript is weakly typed, so method signatures and native support is out. My recommendation is to pass an extensible object as the solitary argument. Inspect and handle the existance of properties on the param object as you wish.

What advantage does this have over arguments? Well it lets you be explicit about your intentions where you call, and unambiguous about the meaning of arg1 and arg2 where you recieve, and it lets you abstract to a custom data object class you can extend functionality to.

function someFunc(params)
{
  var x = params.x || defaultX;
  var y = params.y || defaultY;

  //businesslogic
}

someFunc({x:'foo',y:'bar'});

someFunc({y:'baz'});
annakata
Interesting. Does this work if x or y are boolean 'false' ?
JBRWilkinson
as is no, but consider this an example - `typeof(params.x) != 'undefined' ? params.x : defaultX;` would be more suitable
annakata
A: 

Everyone came close, I think that the real issue here is that in JavaScript you shouldn't change behavior based on parameters being passed in.

Since JavaScript makes all parameters optional you could follow the concise method of using this convention:

function foo(bar, baz) {
    bar = bar || defaultBar;
    baz = baz || defaultBaz;
    .
    .
    .
}
ihumanable
A: 

Personally, I prefer to write the most complex function that would be performed, and then document it in comments so that others know that they do not have to send all the arguments.

//concat(str1, str2 [,str3 [,str4 [,str5]]])
function concat(str1, str2, str3, str4, str5) {
    var str = str1 + str2;
    if(str3 != undefined)
        str += str3;
    if(str4 != undefined)
        str += str4;
    if(str5 != undefined)
        str += str5;
    return str;
}

I have also found situations where the argument order would matter in a normal function, but sometimes I would want to sent the arguments seperately (i.e. I would want to send str3 and str5 but not str4). For this, I use an object and test the known properties

//concat({str1:string, str2:string, str3:string, str4:string, str5:string})
//str3, str4, and str5 are optional
function concat(strngs) {
    var str = strngs.str1 + strngs.str2;
    if(strngs.str3 != undefined)
        str += strngs.str3;
    if(strngs.str4 != undefined)
        str += strngs.str4;
    if(strngs.str5 != undefined)
        str += strngs.str5;
    return str;
}
Mike
A: 

Nice answers.