
"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
-
Type:
typeof undefined; // "undefined" typeof null; // "object" (this is actually a historical bug in JavaScript!)
-
Equality:
null == undefined; // true (loose equality) null === undefined; // false (strict equality)
-
Mathematical operations:
undefined + 1; // NaN null + 1; // 1 (null converts to 0)
-
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)
-
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;
}
- Mutation: It modifies the original object instead of creating a copy.
- Edge Cases: It doesn't handle the case where
arg
itself isundefined
. - Arrays: It doesn't properly handle arrays (though it might work in some cases).
- Null Check: It doesn't check if
arg[key]
isnull
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!