Chris Cirefice
Chris Cirefice

Reputation: 5795

Javascript Coding Practices: Empty Values in Objects

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

Answers (1)

Jan Turoň
Jan Turoň

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

  1. the nullobject default properties are not valid values
  2. the default values don't spoil you operations

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

Related Questions