Reputation: 5795
Just read this highly-related question, and then reflected on what I was using in my own code base for a project at work.
I wrote a simple function to demonstrate this question (using Google Apps Script, thus the calls to Logger.log()
):
function emptyValueTest() {
var object = {
prop1: "real_data",
prop2: "",
prop3: undefined,
prop4: null
}
// What I have used in my code personally. All evaluate to TRUE
if (object.prop2 === "") Logger.log("prop2 was empty");
if (typeof object.prop3 === "undefined") Logger.log("prop3 was undefined");
if (object.prop4 === null) Logger.log("prop4 was null");
// The other solution I've seen. All evaluate to FALSE
if (object.prop2) Logger.log("prop2 was empty");
if (object.prop3) Logger.log("prop3 was undefined");
if (object.prop4) Logger.log("prop4 was null");
}
I've been building a system for the past few months with thousands of lines of code, and I often find myself back-tracking when I see a particular value, to remember what it's previous state was before a particular conditional expression.
Since I'm the one writing the code it isn't too difficult for me to figure it out, because I know the structure of the data, the values that are supposed to be there, etc.
However, this system will be taken over by another developer after I graduate from my university, so I wonder what the best practice is for representing that a property has no data.
For example, my use case is that I have event
Objects which are initialized with empty data and, during the lifetime of a semester, are eventually filled with real data. Personally I use undefined
because it makes more sense to me to read if (typeof data === "undefined")
, having a similar semantic meaning to "if this data is undefined" (i.e. no value yet).
But I wonder, in industry, in libraries, and in personal projects, what are commonly-used methods to make code more readable, understandable? Though the length of the first three conditionals is longer than the latter three, I find the latter three to be ambiguous.
For example, prop2
could have a non-empty string like "data"
, or the value true
, which would cause the latter conditional if (object.prop2)
to evaluate to true
in both cases, because everything in JavaScript is truthy except the following:
And it is entirely possible that data could have ""
, 0
, and false
as valid values, whereas null
, undefined
and (especially) NaN
are probably not typical values for real data.
Obviously I we know the structure of the data then we might impose more strict checks to disambiguate non-empty strings from true. But in a project with thousands of lines of code it seems to me that this gets out of hand fast, especially if the structure of the data changes over time.
So my question: Is there a preferred method of representing empty values in Javascript? And is there a particular reason for the preference (performance, readability, scalability, etc.)?
Upvotes: 1
Views: 362
Reputation: 32912
Javascript is especially insidious in the NaN
constant:
alert(NaN==NaN?"isNaN":"isNotNaN"); // isNotNaN !
The most common way to represent empty object is the null
constant. However, you can run into troubles using it:
var coords = null;
if(coords===null) alert("empty object");
if(coords.x==0 && coords.y==0) alert("[0,0]");
// Uncaught TypeError: Cannot read property 'x' of null
There is a design pattern called Null object: to create an object representing null
value with correct properties:
var nullcoords = {x:-1,y:-1}; // null object
var coords = nullcoords;
if(coords===nullcoords) alert("empty coords");
if(coords.x==0 && coords.y==0) alert("[0,0]");
// no TypeError
As RobG points in the comment, you can afford this attitude iff
However, null
is not a good idea for coords properties since it is not a numeric type and can mess some arithmetic operations (not in JS, though). The below code is more appropriate (and more sophisticated) solution with custom null object handler:
function nullCoordsHandler(obj) {
// any error handling you want
alert("setting default values...");
obj.x = obj.y = 0;
}
function coords() {
var _x, _y;
var defined = false;
Object.defineProperty(this,"x",{
enumerable: true,
get:function() { if(!defined) nullCoordsHandler(this); return _x; },
set:function(value) { defined = true; _x = value; }
});
Object.defineProperty(this,"y",{
enumerable: true,
get:function() { if(!defined) nullCoordsHandler(this); return _y; },
set:function(value) { defined = true; _y = value; }
});
};
var c = new coords();
if(c.x==0 && c.y==0) alert("[0,0]");
// nullCoordsHandler was called to handle the null object situation
Upvotes: 1