Last week, a few of my colleagues and I were discussing how to check if an object has a property in JavaScript. I’ve written about the difference between the in
operator and the .hasOwnProperty()
method before, but I thought I’d revisit it today.
Check if the value is truthy
One way to check if an object has a property is to check if the value of the property is truthy. You can do this using the regular property accessor syntax, i.e. dot notation or bracket notation. This works because when you try to read from a property that doesn’t exist, its value is treated as undefined
, which is falsy.
Consider an object that represents a user. To check if the user has a name, we can simply check if (user.name)
. This works because a non-empty string is considered truthy. If the .name
property didn’t exist, its value would be considered undefined
, which is falsy.
let user = {
name: "jsmith",
};
if (user.name) {
// As long as the value of the `.name` property is truthy, any code nested
// inside this conditional statement will be executed.
}
This approach falls short when you need to allow falsy values. What if the object had an .age
property whose value could be 0
, or an .isOnline
property whose value could be false
? The .age
and .isOnline
properties would be defined, but because their values would be falsy, any code nested inside the conditional statements would not be executed.
let user = {
name: "jsmith",
age: 0,
isOnline: false,
};
if (user.age) {
// The `.age` property is defined, but because its value is falsy, any code
// nested inside this conditional statement will NOT be executed.
}
if (user.isOnline) {
// The `.isOnline` property is defined, but because its value is falsy, any
// code nested inside this conditional statement will NOT be executed.
}
Note that a property may be defined while its value is undefined
. The check for truthiness would fall short here too, since undefined
is falsy.
// The `.size` property is defined but its value is `undefined`.
let universe = {
size: undefined,
};
The in
operator
A safer approach is to use the in
operator. Like property accessors, the in
operator checks the object itself and its prototype chain. Unlike property accessors, it only checks for the presence of a property. It doesn’t bother with its value.
let user = {
name: "jsmith",
age: 0,
isOnline: false,
};
if ("age" in user) {
// The `.age` property is defined, and because we're using the `in` operator,
// any code nested inside this conditional statement will be executed, even
// though the value of the property is falsy.
}
if ("isOnline" in user) {
// The `.isOnline` property is defined, and because we're using the `in`
// operator, any code nested inside this conditional statement will be
// executed, even though the value of the property is falsy.
}
The Object.hasOwn()
method
The third way to check if an object has a property is to use the static Object.hasOwn()
method. The difference between this method and the in
operator is that the Object.hasOwn()
method does not traverse an object’s prototype chain. It only considers an object’s own properties.
let user = {
name: "jsmith",
age: 0,
isOnline: false,
};
if (Object.hasOwn(user, "age")) {
// Because the `.age` property is defined directly on the object and not on
// its prototype chain, any code nested inside this conditional statement will
// be executed, even though the value of the property is falsy.
}
if (Object.hasOwn(user, "isOnline")) {
// Because the `.isOnline` property is defined directly on the object and not
// on its prototype chain, any code nested inside this conditional statement
// will be executed, even though the value of the property is falsy.
}
Own vs inherited properties
Let’s look at an example that illustrates the difference between the in
operator and the Object.hasOwn()
method. All objects inherit the Object.prototype.toString()
method through their prototype chain. When we check for this property, the in
operator will return true
, while the Object.hasOwn()
method will return false
. This is because the property is not defined directly on the object, but on its prototype chain.
let user = {
name: "jsmith",
age: 0,
isOnline: false,
};
if ("toString" in user) {
// Although the `.toString()` method is not defined on the object directly, it
// is defined on the object's prototype chain, so any code nested inside this
// conditional statement will be executed.
}
if (Object.hasOwn(user, "toString")) {
// Because the `.toString()` method is defined on the object's prototype chain
// and not directly on the object, any code nested inside this conditional
// statement will NOT be executed.
}
This applies to your own code too, not just standard properties and methods. Consider the following class. It has an .#isOnline
private instance property with an initial value of false
. The .isOnline()
method returns the value of the private property while the .logIn()
and .logOut()
methods set it.
class User {
#isOnline = false;
isOnline() {
return this.#isOnline;
}
logIn() {
this.#isOnline = true;
return this.#isOnline;
}
logOut() {
this.#isOnline = false;
return this.#isOnline;
}
}
If we were to check if (Object.hasOwn(user, "isOnline"))
, it would evaluate to false
. However, if ("isOnline" in user)
would evaluate to true
, because the .isOnline()
, .logIn()
, and .logOut()
methods are added to the object’s prototype. Only the .#isOnline
private instance property is added directly to the User
object instance.
let user = new User();
console.log(Object.hasOwn(user, "isOnline")); // false
console.log("isOnline" in user); // true
JavaScript’s class
syntax still uses prototypal inheritance internally. The .isOnline()
, .logIn()
, and .logOut()
methods are added to the User
class’ Function.prototype.prototype
property (classes are a special type of function). The value of this property is used as the prototype of the newly constructed object when new User()
is invoked.
The following is roughly* what the class translates to. Notice that only the ._isOnline
property will be added directly to a User
object instance. The methods are added to the User
function’s .prototype
property (functions are a type of object).
function User() {
this._isOnline = false;
}
User.prototype.isOnline = function () {
return this._isOnline;
};
User.prototype.logIn = function () {
this._isOnline = true;
return this._isOnline;
};
User.prototype.logOut = function () {
this._isOnline = false;
return this._isOnline;
};
Summary
- If falsy values aren’t allowed, and you don’t care if the property is on the object or its prototype chain, just check if the value of the property is truthy.
- If the value of the property could be falsy, and you don’t care if the property is on the object or its prototype chain, use the
in
operator. - If the value of the property could be falsy, and the property needs to be directly on the object, use the
Object.hasOwn()
method.