tags:

views:

152

answers:

3

I know very well that null and undefined are distinct in JavaScript. However, I can't seem to decide whether or not use that fact when my own functions are passed one of those as its argument.

Or, expressed in a different way, should myFoo(undefined) return the same thing as myFoo(null)?

Or, in yet another case, since myBar(1, 2, 3) is (almost) the same thing as myBar(1, 2, 3, undefined, undefined), should myBar(1, 2, 3, null, null) return the same thing as myBar(1, 2, 3)?

I feel that there's potential for confusion in both cases and that a library should probably follow a convention when handling null/undefined.

I'm not really asking for personal opinions (so please express those as comments rather than answers). I'm asking if anyone knows if there is a best practice that one should stick to when it comes to handling this distinction. References to external sources are very welcome!

A: 

Its entirely up to you how you handle arguments passed to your function, so its up to you. If you want to check if an argument is null you use

if (myVar === null) { 

and if you want to check if an argument is undefined you use

if (typeof myVar === "undefined") {

If the expected argument is anything other than 0, null or undefined, then you can check for this using

if (myVar) { 

So whether or not your myFoo(null) should behave the same as myFoo(undefined) is entirely up to how you handle these internally.

When it comes to extra parameters, this has no effect other than the arguments collection being larger than expected.

Sean Kinsey
I *suspect* a comparison with `null` to be (slightly) faster than a comparison with string `"undefined"`.
Marcel Korpel
@Marcel And your point is? that comparing an apple to an apple is faster than comparing an orange to an orange? Or that the typeof call takes longer time? Of what importance is this?
Sean Kinsey
From a machine point of view, doing a `typeof` that results in a string and comparing a string with a string is faster than comparing something to `null` (in assembly language most processors have a `JZ` opcode). The OP asked for differences.
Marcel Korpel
That is of little use here - this piece of 'knowledge' will not help you handle null or undefined values better, which I believe is what he asked for..
Sean Kinsey
-1 for not reading the question
Theo
This does not answer the question. As previously stated, I know the difference between the two and that it's up to me to handle it as I desire; it's my program :) What I'm asking is if it's a good idea to handle them differently or not, and why.
Jakob
@Jakob: Again, its entirely up to you and strongly depends on the type of arguments you expect. If your code does not care whether `arg1` is `0`, `undefined` or `null` then just cast it to a boolean, if not then check for the exact value. The short answer: the best practice is to use the correct method. Would you use `-` to add two numbers` or `*` to divide? Probably not, so how can you then ask for a best practise on 'which operator should I use for doing math?'?
Sean Kinsey
@Theo, but I did and my answer is still correct. You cannot promote a best practice without knowing for what this practice will be used for, and therefor I opted to point out that it is entirely dependent on the code at hand and the choices made by the developer.
Sean Kinsey
@Sean I could write an answer that explains the political situation in Sudan, while correct in itself it would still not answer the question. He explicitly says that he's not after an answer such as yours, so -1 for not reading the question.
Theo
@Theo, seriously, are you of the opinion that the answer the OP seeks actually exist? The premise of my answer is that if the OP *understands* what he is doing, then there *is no need* for a *best practice* (even if one could be presented for this case).
Sean Kinsey
+2  A: 

I'd say that while, most of the time, there is little value in distinguishing between the two, the cases where there is value tend to be quite interesting.

Take, for example, a function which can be given a callback. undefined might indicate that some default callback should be used (as if the parameter weren't specified), but null could indicate that no callback should be made at all:

function asyncWorker(data, callback, timeout) {
    if (typeof callback === "undefined") {
        callback = function() { $("#log").append("<p>Done!</p>"); };
    }

    // ...

    if (callback) callback();
}

asyncWorker([1, 2, 3]); // default callback, no timeout
asyncWorker([4, 5, 6], null); // no callback, no timeout
asyncWorker([7, 8, 9], undefined, 10000); // default callback, 10s timeout

Of course, false or 0 could be used instead of null here, but that might not be the case in a more complex example. And whether your code benefits from the additional parameter complexity is entirely up to you. :-)

Ben Blank
Using one of the special values as "nothing" and one as "default" sure is interesting. Great example! Is this concept common in JavaScript libraries? (I'm only used to jQuery, and even that I've not used very much).
Jakob
I've not seen it widely used, but a few hours after writing this answer, I had occasion to distinguish between `null` and `undefined` myself in exactly this manner for an event library I'm writing at work which supports jQuery-style optional parameters (e.g. `method(parm1[, parm2], parm3)`.
Ben Blank
Since when did optional arguments become 'jQuery-style'? =)
Sean Kinsey
@Sean — When "jQuery-style" was made a project requirement. ;-)
Ben Blank
@Ben - I don't get it..
Sean Kinsey
+1  A: 

Best practice for handling arguments

  • define the expected arguments, what they will be used for, and the types of these
  • decide how they are accessed, either via the formal arguments, or through the arguments collection
  • define whether values outside the expected range should result in default values
  • verify that the arguments are within range before processing
  • for boolean values, decide whether they must be true booleans, or only truthy/falsy

Step 2, 3 and 4 is of most importance to you in this case

How to access the arguments
This is something you will need to select based on what the method does, and your strategy for handling a variable amount of arguments.

Take this for example

function foo() {
    var args = arguments.length == 1 ? 
        [document.body].concat([arguments[0]]) : 
        Array.prototype.slice.call(arguments);

    args[0].style.backgroundColor = args[1];
}

foo(document.body, "red");
foo("blue");​

True boolean or truthy/falsy
How to test for values depends largely on how your code is set up

function foo(elem, color) {
    if (!color) { // the correct test here should be 'typeof color == "undefined"'
        color = "green";
    }
    elem.style.backgroundColor = color;
}

foo(document.body, "red"); //set the background color to red
foo(document.body); //set the background color to the default green
foo(document.body, "");​ //remove the background color !This will fail!

The last statement will wrongly use the default value instead of the provided, even though the provided is within the expected range.

When it comes to handling undefined values remember that there is no difference in foo(undefined); and foo(); except that the length of the arguments collection will be different. How this is handled (and if it needs to be) is dependent on how you access the arguments.

Sean Kinsey