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!
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!
Share Improve this question edited Jun 8, 2010 at 9:53 Jakob asked Jun 2, 2010 at 16:48 JakobJakob 24.4k8 gold badges47 silver badges58 bronze badges 2 |3 Answers
Reset to default 11 +100I'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. :-)
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.
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.
myBar(1, 2, 3)
is not the same thing asmyBar(1, 2, 3, undefined, undefined)
: thearguments
object will have length of 3 in the first case and 5 in the second. – Tim Down Commented Jun 2, 2010 at 16:51arguments
object. – Jakob Commented Jun 2, 2010 at 16:56