Rohit Nandi

Full Stack Engineer

Null vs Undefined in JavaScript
Null vs Undefined (Published on 20th April 2025)

"Null-ifying the Void: When undefined Ghosts Your JSON"

Have you ever been ghosted? Well, JavaScript has two ways of expressing nothingness that sometimes ghost each other: null and undefined. While they might seem interchangeable at first glance, these two values behave quite differently, especially when it comes to JSON serialization.

In this post, we'll explore the key differences between null and undefined, and implement a handy utility function to convert all those sneaky undefined values to null in your objects.


The Tale of Two Nothings

JavaScript is unique among programming languages for having not one, but two distinct "nothing" values. This peculiarity often leads to confusion, bugs, and heated debates among developers. Let's break down what each one means:

undefined

undefined represents a variable that has been declared but hasn't been assigned a value yet. It's JavaScript's way of saying, "This thing exists, but it has no value."

let ghost; // automatically assigned undefined
console.log(ghost); // undefined

function returnNothing() {
  // no return statement
}
console.log(returnNothing()); // undefined

const obj = {};
console.log(obj.nonExistentProperty); // undefined

null

null, on the other hand, is an explicit assignment. It's a developer's way of saying, "I intentionally set this to nothing."

let deliberatelyEmpty = null;
console.log(deliberatelyEmpty); // null

Key Differences Between null and undefined

  1. Type:

    typeof undefined; // "undefined"
    typeof null;      // "object" (this is actually a historical bug in JavaScript!)
    
  2. Equality:

    null == undefined;  // true (loose equality)
    null === undefined; // false (strict equality)
    
  3. Mathematical operations:

    undefined + 1; // NaN
    null + 1;      // 1 (null converts to 0)
    
  4. Default function parameters:

    function greet(name = 'stranger') {
      return `Hello, ${name}!`;
    }
    greet(undefined); // "Hello, stranger!" (default kicks in)
    greet(null);      // "Hello, null!" (null is treated as a valid value)
    
  5. JSON serialization (the star of our show today):

    JSON.stringify({a: null});      // '{"a":null}'
    JSON.stringify({a: undefined}); // '{}'
    JSON.stringify([null]);         // '[null]'
    JSON.stringify([undefined]);    // '[null]'
    

The JSON Serialization Conundrum

As you can see from the last example, undefined values get completely omitted from objects during JSON serialization, while null values are preserved. In arrays, both undefined and null are serialized as null.

This inconsistency can create headaches when you're working with APIs, especially when there's an expectation about the shape of your data. If your server expects a property to always be present (even if it's null), sending an object with undefined values can break this contract.

Implementing undefinedToNull()

To address this issue, let's implement a utility function that converts all undefined values in an object to null:

function undefinedToNull(arg) {
  // If arg is undefined, return null
  if (arg === undefined) return null;
  
  // If arg is not an object or is null, return it as is
  if (arg === null || typeof arg !== 'object') return arg;
  
  // Handle arrays
  if (Array.isArray(arg)) {
    return arg.map(item => undefinedToNull(item));
  }
  
  // Handle objects
  const result = {};
  for (const key in arg) {
    if (Object.prototype.hasOwnProperty.call(arg, key)) {
      result[key] = undefinedToNull(arg[key]);
    }
  }
  return result;
}

Let's test our function:

undefinedToNull({a: undefined, b: 'BFE.dev'});
// {a: null, b: 'BFE.dev'}

undefinedToNull({a: ['BFE.dev', undefined, 'bigfrontend.dev']});
// {a: ['BFE.dev', null, 'bigfrontend.dev']}

What's Wrong with the Initial Solution?

The initial solution had a few issues:

function undefinedToNull(arg) {
  // your code here
  for (let key in arg) {
    if (arg[key] === undefined) {
      arg[key] = null;
    }
    if (typeof arg[key] === "object") {
      undefinedToNull(arg[key]);
    }
  }
  return arg;
}
  1. Mutation: It modifies the original object instead of creating a copy.
  2. Edge Cases: It doesn't handle the case where arg itself is undefined.
  3. Arrays: It doesn't properly handle arrays (though it might work in some cases).
  4. Null Check: It doesn't check if arg[key] is null before checking if it's an object.

When to Use Each One

So when should you use null vs undefined?

  • Use undefined when a value doesn't exist or hasn't been set yet.
  • Use null when you want to explicitly indicate the absence of a value.

When working with APIs or serializing data, it's often safer to use null for missing values to ensure consistency across different systems.

Conclusion

The distinction between null and undefined might seem like a quirk of JavaScript, but understanding their differences can help you write more predictable code, especially when it comes to data serialization.

The undefinedToNull function we implemented provides a simple way to ensure your objects maintain their structure when converted to JSON, preventing those pesky undefined values from ghosting your properties.

Remember: in JavaScript, nothing is never just nothing—it's either intentionally nothing (null) or accidentally nothing (undefined)!

Have you encountered any other quirky behaviors with null and undefined? Share your experiences in the comments below!