266

I have now seen 2 methods for determining if an argument has been passed to a JavaScript function. I'm wondering if one method is better than the other or if one is just bad to use?

 function Test(argument1, argument2) {
      if (Test.arguments.length == 1) argument2 = 'blah';

      alert(argument2);
 }

 Test('test');

Or

 function Test(argument1, argument2) {
      argument2 = argument2 || 'blah';

      alert(argument2);
 }

 Test('test');

As far as I can tell, they both result in the same thing, but I've only used the first one before in production.

Another Option as mentioned by Tom:

function Test(argument1, argument2) {
    if(argument2 === null) {
        argument2 = 'blah';
    }

    alert(argument2);
}

As per Juan's comment, it would be better to change Tom's suggestion to:

function Test(argument1, argument2) {
    if(argument2 === undefined) {
        argument2 = 'blah';
    }

    alert(argument2);
}
Community
  • 1
  • 1
Darryl Hein
  • 142,451
  • 95
  • 218
  • 261
  • 7
    An argument that was not passed comes in as undefined. Testing with strict equality against null will fail. You should use strict equality with undefined. – Ruan Mendes Jan 17 '11 at 20:17
  • It's really the same. If you always have a static number of arguments then go with the second method, otherwise it's probably easier to iterate using the arguments array. – Luca Matteis Jan 04 '09 at 17:43
  • 24
    Remember: `argument2 || 'blah';` will result in 'blah' if `argument2` is `false`(!), not simply if it is undefined. **If `argument2` is a boolean, and the function is passed `false` for it, that line will return 'blah' despite `argument2` being properly defined**. – Sandy Gifford Apr 26 '13 at 17:39
  • 9
    @SandyGifford: Same problem if `argument2` is `0`, `''`, or `null`. – rvighne Jun 16 '14 at 17:21
  • 1
    @rvighne Very true. Javascript's unique interpretation of objects and casting is all at once it's best and worst parts. – Sandy Gifford Jun 16 '14 at 18:34

14 Answers14

296

There are several different ways to check if an argument was passed to a function. In addition to the two you mentioned in your (original) question - checking arguments.length or using the || operator to provide default values - one can also explicitly check the arguments for undefined via argument2 === undefined or typeof argument2 === 'undefined' if one is paranoid (see comments).

Using the || operator has become standard practice - all the cool kids do it - but be careful: The default value will be triggered if the argument evaluates to false, which means it might actually be undefined, null, false, 0, '' (or anything else for which Boolean(...) returns false).

So the question is when to use which check, as they all yield slightly different results.

Checking arguments.length exhibits the 'most correct' behaviour, but it might not be feasible if there's more than one optional argument.

The test for undefined is next 'best' - it only 'fails' if the function is explicitly called with an undefined value, which in all likelyhood should be treated the same way as omitting the argument.

The use of the || operator might trigger usage of the default value even if a valid argument is provided. On the other hand, its behaviour might actually be desired.

To summarize: Only use it if you know what you're doing!

In my opinion, using || is also the way to go if there's more than one optional argument and one doesn't want to pass an object literal as a workaround for named parameters.

Another nice way to provide default values using arguments.length is possible by falling through the labels of a switch statement:

function test(requiredArg, optionalArg1, optionalArg2, optionalArg3) {
    switch(arguments.length) {
        case 1: optionalArg1 = 'default1';
        case 2: optionalArg2 = 'default2';
        case 3: optionalArg3 = 'default3';
        case 4: break;
        default: throw new Error('illegal argument count')
    }
    // do stuff
}

This has the downside that the programmer's intention is not (visually) obvious and uses 'magic numbers'; it is therefore possibly error prone.

Christoph
  • 164,997
  • 36
  • 182
  • 240
  • 47
    You should actually check typeof argument2 === "undefined", in case someone defines "undefined". – JW. Jan 04 '09 at 18:19
  • 146
    I'll add a notice - but what sick bastards do things like that? – Christoph Jan 04 '09 at 18:24
  • 1
    Nobody that I know, so I usually use === undefined. But someone pointed out to me (on a similar thread) that undefined isn't actually a JavaScript keyword, so this is kind of a hack. – JW. Jan 04 '09 at 19:38
  • 14
    undefined is a variable in the global space. It is slower to lookup that variable in the global scope than a variable in a local scope. But the fastest is to use typeof x === "undefined" – some Jan 04 '09 at 20:40
  • 7
    Interesting. On the other hand, the comparison === undefined might be faster than the string comparison. My tests seem to indicate that you're right, though: x === undefined needed ~1.5x the time of typeof x === 'undefined' – Christoph Jan 04 '09 at 21:26
  • That is because strings in JavaScript are immutable, so string comparison is just testing pointers, not testing content. – Ruan Mendes Jan 17 '11 at 20:19
  • 2
    @Juan: immutable strings don't imply unique strings, and only the latter would mean equivalence of string equality and pointer equality; take the Java VM as an example for a language runtime with immutable, but - in the general case - non-unique strings – Christoph Jan 17 '11 at 22:01
  • @Cristoph, that is very interesting. I always assumed immutable strings would go along with unique strings. Why would you create two objects with the same contents if they can't be changed? – Ruan Mendes Jan 17 '11 at 22:25
  • 1
    @Juan: unique strings penalise string creation - if a string's contents aren't known at compile-time, you'll have to hit a string cache (some kind of hashtable or search tree) at runtime to guarantee that you don't create a duplicate; afaik even if they don't use unique strings, many string handling frameworks choose immutability over alternatives like copy-on-write for various reasons (simpler algorithms, easier memory management, ...) – Christoph Jan 18 '11 at 00:12
  • 6
    @Cristoph: after reading your comment, I asked around. I was able to prove that string comparison is surely not (only) by pointer comparison since comparing a gigantic string takes longer than a small string. However, comparing two gigantic strings is really slow only if they were built up with concatenation, but really fast if the second one was created as an assignment from the the first. So it probably works by a pointer test followed by letter by letter testing So, yeah, I was full of crap :) thanks for enlightening me... – Ruan Mendes Jan 18 '11 at 01:08
  • folks, what if the function's last parameter is a callback()? – Cmag Dec 06 '14 at 20:57
  • instead of writing the keyword "undefined", you can write "void 0". This saves a couple of characters. Google Closure Compiler uses that trick. Ie: (undefined===argument2) can be rewritten to (void 0===argument2) – rosell.dk Aug 18 '16 at 07:20
  • If you are going to use that function twice, the least-characters-solution is this to deligate it to a helper: function u(a){return void 0===a}. – rosell.dk Aug 18 '16 at 07:23
18

If you are using jQuery, one option that is nice (especially for complicated situations) is to use jQuery's extend method.

function foo(options) {

    default_options = {
        timeout : 1000,
        callback : function(){},
        some_number : 50,
        some_text : "hello world"
    };

    options = $.extend({}, default_options, options);
}

If you call the function then like this:

foo({timeout : 500});

The options variable would then be:

{
    timeout : 500,
    callback : function(){},
    some_number : 50,
    some_text : "hello world"
};
Greg Tatum
  • 1,122
  • 10
  • 12
16

This is one of the few cases where I find the test:

if(! argument2) {  

}

works quite nicely and carries the correct implication syntactically.

(With the simultaneous restriction that I wouldn't allow a legitimate null value for argument2 which has some other meaning; but that would be really confusing.)

EDIT:

This is a really good example of a stylistic difference between loosely-typed and strongly-typed languages; and a stylistic option that javascript affords in spades.

My personal preference (with no criticism meant for other preferences) is minimalism. The less the code has to say, as long as I'm consistent and concise, the less someone else has to comprehend to correctly infer my meaning.

One implication of that preference is that I don't want to - don't find it useful to - pile up a bunch of type-dependency tests. Instead, I try to make the code mean what it looks like it means; and test only for what I really will need to test for.

One of the aggravations I find in some other peoples' code is needing to figure out whether or not they expect, in the larger context, to actually run into the cases they are testing for. Or if they are trying to test for everything possible, on the chance that they don't anticipate the context completely enough. Which means I end up needing to track them down exhaustively in both directions before I can confidently refactor or modify anything. I figure that there's a good chance they might have put those various tests in place because they foresaw circumstances where they would be needed (and which usually aren't apparent to me).

(I consider that a serious downside in the way these folks use dynamic languages. Too often people don't want to give up all the static tests, and end up faking it.)

I've seen this most glaringly in comparing comprehensive ActionScript 3 code with elegant javascript code. The AS3 can be 3 or 4 times the bulk of the js, and the reliability I suspect is at least no better, just because of the number (3-4X) of coding decisions that were made.

As you say, Shog9, YMMV. :D

Nicolás Ozimica
  • 9,481
  • 5
  • 38
  • 51
dkretz
  • 37,399
  • 13
  • 80
  • 138
  • 1
    if(!argument2) argument2 = 'default' is equivalent to argument2 = argument2 || 'default' - I find the second version visually more pleasing... – Christoph Jan 04 '09 at 18:01
  • 6
    And I find it more verbose and distracting; but it's personal preference, I'm sure. – dkretz Jan 04 '09 at 18:02
  • 4
    @le dorfier: it also precludes the use of empty strings, 0, and boolean false. – Shog9 Jan 04 '09 at 18:03
  • @le dorfier: beyond aesthetics, there's one key difference: the latter effectively creates a second path of execution, which might tempt careless maintainers to add behavior beyond the simple assignment of a default value. YMMV, of course. – Shog9 Jan 04 '09 at 18:04
  • 4
    what if parameter2 is a boolean === false; or a function {return false;} ? – fresko Oct 12 '16 at 10:15
  • Even if you pass a callback, @fresko, that callback would equate to true, since it's a function. The function wouldn't be called, so it doesn't return a falsey value. – Bronzdragon Dec 13 '18 at 16:18
  • This is by far the best answer! And with a full stop! (I really wonder why all this fuss (including the long comment that accompanies it! :)) – Apostolos Mar 02 '21 at 07:43
13

In ES6 (ES2015) you can use Default parameters

function Test(arg1 = 'Hello', arg2 = 'World!'){
  alert(arg1 + ' ' +arg2);
}

Test('Hello', 'World!'); // Hello World!
Test('Hello'); // Hello World!
Test(); // Hello World!
andriy2
  • 151
  • 1
  • 7
  • This answer is really interesting and could be useful to op. Though it doesn't really _answers_ the question – Ulysse BN Jul 23 '17 at 22:21
  • As I see - he wants to define arg if it was not defined, so I posted some helpful information – andriy2 Jul 23 '17 at 22:26
  • 1
    My point is you're not answering to _How best to determine if an argument is not sent to the JavaScript function_. But the question **could** be answered using default arguments: for instance naming you arguments with `"default value"` and checking if the value is indeed `"default value"`. – Ulysse BN Jul 23 '17 at 22:54
9
url = url === undefined ? location.href : url;
Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
Yasen
  • 3,400
  • 1
  • 27
  • 22
  • 1
    Bare bones answer. Some explanation wouldn't hurt. – Paul Rooney Feb 13 '17 at 00:50
  • 1
    It's a ternary operator that concisely says: If url is undefined (missing), set the url variable to be location.href (the current web page), otherwise set the url variable to be the defined url. – rmooney Sep 04 '19 at 00:16
7

There are significant differences. Let's set up some test cases:

var unused; // value will be undefined
Test("test1", "some value");
Test("test2");
Test("test3", unused);
Test("test4", null);
Test("test5", 0);
Test("test6", "");

With the first method you describe, only the second test will use the default value. The second method will default all but the first (as JS will convert undefined, null, 0, and "" into the boolean false. And if you were to use Tom's method, only the fourth test will use the default!

Which method you choose really depends on your intended behavior. If values other than undefined are allowable for argument2, then you'll probably want some variation on the first; if a non-zero, non-null, non-empty value is desired, then the second method is ideal - indeed, it is often used to quickly eliminate such a wide range of values from consideration.

Shog9
  • 156,901
  • 35
  • 231
  • 235
4

I'm sorry, I still yet cant comment, so to answer Tom's answer... In javascript (undefined != null) == false In fact that function wont work with "null", you should use "undefined"

Luca Matteis
  • 29,161
  • 19
  • 114
  • 169
  • 1
    And Tom got two upvotes for a wrong answer - it's always nice to know how good these community systems work ;) – Christoph Jan 04 '09 at 17:53
4

There is a tricky way as well to find, whether a parameter is passed to a function or not. Have a look at the below example:

this.setCurrent = function(value) {
  this.current = value || 0;
};

This necessary means that if the value of value is not present/passed - set it to 0.

Pretty cool huh!

Zameer Ansari
  • 28,977
  • 24
  • 140
  • 219
  • 1
    This actually means "if `value` is equivalent to `false`, set it to 0." This is a subtle but very important distinction. – Charles Wood Feb 07 '18 at 15:27
  • @CharlesWood Value not passed / present means is false only – Zameer Ansari Feb 09 '18 at 13:13
  • 1
    Sure, if that fits the requirement for your function. But if, for example, your parameter is boolean, then `true` and `false` are valid values, and you may want to have a third behavior for when the parameter is not passed at all (especially if the function has more than one parameter). – Charles Wood Feb 11 '18 at 03:44
  • 1
    And I should acknowledge that this is a huge argument in computer science and may just end up being a matter of opinion :D – Charles Wood Feb 11 '18 at 03:45
  • @CharlesWood sorry for being late to the party. I suggest you to add these in the answer itself with edit option – Zameer Ansari Feb 17 '18 at 10:36
3

Why not using the !! operator? This operator, placed before the variable, turn it to a boolean (if I've understood well), so !!undefined and !!null (and even !!NaN, which can be quite interesting) will return false.

Here is an exemple:

function foo(bar){
    console.log(!!bar);
}

foo("hey") //=> will log true

foo() //=> will log false
RallionRl
  • 2,453
  • 2
  • 10
  • 9
  • 1
    Does not work with boolean true, zero and empty string. For example, foo(0); will log false, but foo(1) will log true – rosell.dk Aug 18 '16 at 07:15
3

Sometimes you want undefined as a possible argument but you still have situations where the argument may not be passed. In that case you can use arguments.length to check how many arguments were passed.


// Throw error if the field is not matching our expectations
function testField(label, fieldValue, expectedValue) {
    console.log(arguments) // Gives: [Arguments] { '0': 'id', '1': 1, '2': undefined }
    if(arguments.length === 2) {
        if(!fieldValue) {
            throw new Error(`Field "${label}" must have a value`)
        }
    }

    else if(expectedValue === undefined) {
        if(fieldValue !== undefined) {
            throw Error(`Field "${label}" must NOT have a value`)
        }
    }

    // We stringify so our check works for objects as well
    else {
        if(JSON.stringify(fieldValue) !== JSON.stringify(expectedValue)) {
            throw Error(`Field "${label}" must equal ${expectedValue} but was ${fieldValue}`)
        }
    }
}

testField('id', 12) -> Passes, we don't want id to be blank

testField('id', undefined, undefined) -> Passes, we want id to be undefined

testField('id', 12, undefined) -> Errors, we wanted id to be undefined

Gilbert
  • 2,699
  • 28
  • 29
2

It can be convenient to approach argument detection by evoking your function with an Object of optional properties:

function foo(options) {
    var config = { // defaults
        list: 'string value',
        of: [a, b, c],
        optional: {x: y},
        objects: function(param){
           // do stuff here
        }
    }; 
    if(options !== undefined){
        for (i in config) {
            if (config.hasOwnProperty(i)){
                if (options[i] !== undefined) { config[i] = options[i]; }
            }
        }
    }
}
1nfiniti
  • 2,032
  • 14
  • 19
1

Some times you may also want to check for type, specially if you are using the function as getter and setter. The following code is ES6 (will not run in EcmaScript 5 or older):

class PrivateTest {
    constructor(aNumber) {
        let _aNumber = aNumber;

        //Privileged setter/getter with access to private _number:
        this.aNumber = function(value) {
            if (value !== undefined && (typeof value === typeof _aNumber)) {
                _aNumber = value;
            }
            else {
                return _aNumber;
            }
        }
    }
}
nbloqs
  • 3,152
  • 1
  • 28
  • 49
1
function example(arg) {
  var argumentID = '0'; //1,2,3,4...whatever
  if (argumentID in arguments === false) {
    console.log(`the argument with id ${argumentID} was not passed to the function`);
  }
}

Because arrays inherit from Object.prototype. Consider ⇑ to make the world better.

-1

fnCalledFunction(Param1,Param2, window.YourOptionalParameter)

If above function is called from many places and you are sure first 2 parameters are passed from every where but not sure about 3rd parameter then you can use window.

window.param3 will handle if it is not defined from the caller method.