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.