There is a common error in JavaScript that occurs when you try to access a property or a method of a variable that is either null
or undefined
.
let user = {
name: "Alice",
address: {
street: "Main Street",
city: "New York",
},
job: null
};
// TypeError: Cannot read properties of null
// console.log(user.job.name);
// TypeError: Cannot read properties of undefined
// console.log(user.x.y)
There is no definitive answer to when you should check if an object property is null
, undefined
, or exists, as it depends on the context and the purpose of your code. It’s critical to fully understand following methods before using any of them.
typeof
operator to check if a property is undefined
. It will return "undefined"
for both undefined
and undeclared values, so it cannot tell if a property exists or not. It will return "object"
for both null
and object values, so it cannot distinguish between them.let obj = {a: 1};
console.log(typeof obj); // "object"
console.log(typeof obj.a); // "number"
console.log(typeof obj.b); // "undefined"
console.log(typeof null); // "object"
==
, ===
) to check if property is null
, undefined
, or both.The equality operator (==
) will compare two values for equality after doing any necessary type conversions. This means that null
and undefined
are equal to each other, but not to anything else.
null == undefined // true
null == 0 // false
null == false // false
null == "" // false
undefined == 0 // false
undefined == false // false
undefined == "" // false
The strict equality operator (===
) will compare two values for equality without doing any type conversion. This means that null
and undefined
are only equal to themselves, and not to any other value.
null === undefined // false
null === 0 // false
null === false // false
null === "" // false
undefined === 0 // false
undefined === false // false
undefined === "" // false
The strict equality operator is usually preferred over the equality operator, as it avoids unexpected results due to type coercion.
let person = {
name: "Alice",
age: null
};
console.log(person.name === null); // false
console.log(person.age === null); // true
console.log(person.address === null); // false
console.log(person.address === undefined); // true
null
or undefined
. Be aware that it will also treat other falsy values (such as false
, 0
, ""
, NaN
) as equivalent.const person = {
name: "John Doe",
age: 25,
address: null,
phone: undefined,
email: "", // empty string
};
for (let prop in person) {
if (person[prop]) {
console.log(`${prop}: ${person[prop]}`);
}
}
// name: John Doe
// age: 25
_.isNil()
function to check if object property is null
or undefined
.var _ = require("lodash");
var obj = {
name: "Charlie",
age: 30,
hobbies: ["cooking", "biking", "gardening"],
address: null,
score: 0,
email: undefined
};
for (var prop in obj) {
if (_.isNil(obj[prop])) {
console.log(prop + ": " + obj[prop]);
}
}
// address: null
// email: undefined
in
operator or hasOwnProperty
method to check if a property exists. The in
operator returns true if the property is found in the object or its prototype chain. The hasOwnProperty
method returns true
if the property is found in the object itself, and false if it is inherited or not found.let obj = { prop1: "value1", prop2: undefined };
console.log("prop1" in obj); // true
console.log("prop2" in obj); // true
console.log("prop3" in obj); // false
console.log("toString" in obj); // true, inherited from Object.prototype
console.log(obj.hasOwnProperty("prop1")); // true
console.log(obj.hasOwnProperty("prop2")); // true
console.log(obj.hasOwnProperty("prop3")); // false
console.log(obj.hasOwnProperty("toString")); // false, inherited from Object.prototype
===
or typeof
to check undefined
Using ===
to check undefined
is safe, as long as you are sure that the variable or property you are checking has been declared. However, if you try to access a variable or property that does not exist, you will get a ReferenceError.
// x is declared but not assigned a value
var x;
console.log(x === undefined); // true
// y is not declared
console.log(y === undefined); // ReferenceError: y is not defined
To avoid this error, you can use the typeof
operator, which returns a string indicating the type of the operand. If the operand is undefined or not declared, it will return “undefined” as well.
// x is declared but not assigned a value
var x;
console.log(typeof x === "undefined"); // true
// y is not declared
console.log(typeof y === "undefined"); // true
Another reason to use typeof
instead of ===
is that the global variable undefined
can be overwritten in ES3, which can cause unexpected results when comparing with ===
.
// In ES3, undefined can be redefined
window.undefined = "foo";
console.log("foo" === undefined); // true
// Using typeof is safer
console.log(typeof "foo" === "undefined"); // false
However, this issue has been fixed in ES5, and undefined is non-writable in modern browsers.
Accessing deeply nested properties is error prone, you need to check null
and undefined
on every step of the chain, not just the target property.
It’s recommended to using optional chaining (?.
) to access properties or methods of a variable that might be null
or undefined
. This operator returns undefined
if any part of the chain is null
or undefined
, instead of throwing an error.
let user = {
name: "Alice",
address: {
street: "Main Street",
city: "New York",
},
};
// Without optional chaining, we need to check if user and user.address exist
console.log(user && user.address && user.address.street); // Main Street
// TypeError: Cannot read properties of undefined
// console.log(user.job.company);
// With optional chaining, we can write it more concisely
console.log(user?.address?.street); // Main Street
console.log(user?.job?.company); // undefined
You can use optional chaining in combination with truthy evaluation, equality operators, and typeof to check if a property value or nullish coalescing operator (??
) to provide a default value.