How to Check if an Object Has a Property in JavaScript
Published: 2023-11-18 23:49:25

In JavaScript, there are different ways to check if an object has a specific property. The main differences between these approaches, besides performance and syntax, are the access to own prototypes or inherited properties from the prototype chain.

Any value in javaScript, with the exception with `undefined` and `null` has a prototype chain, since primitive values are not "by definition" an object, javaScript coerces or boxes the primitive value in a corresponding object. So the values has properties, methods, constructor and prototype inherited from the prototype chain.

![prototype chain of string](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qls4obf5msj4xcfg7z80.png)

Some of the next strategies involve checking both the properties directly belonging to the object (own properties) and those inherited from prototypes.

## Using `in` Operator

The `in` operator returns true if the object contains the property with specified name. Checking in _own prototypes_ or its _prototype chain_ (inherited properties)

`code`
```javascript
const user = { name: 'Alex', age: undefined }
const hasName = 'name' in user
const hasSurname = 'surname' in user
const hasAge = 'age' in user
```

`test`
```javascript
test('user should have name property', () => {
 expect(hasName).toBeTruthy()
})

test('user should not have surname property', () => {
 expect(hasSurname).toBeFalsy()
})

test('user should have age property', () => {
 expect(hasAge).toBeTruthy()
})

test('user should have inherited properties', () => {
 expect('valueOf' in user).toBeTruthy()
})
```
✅ PASS user should have name property
✅ PASS user should NOT have surname property
✅ PASS user should have age property
✅ PASS user should have inherited properties

NOTE: In operator checks the property and not the value assigned, for this reason returns _true_ for _age property_ even when is _undefined_.

## 🧐 Comparing with `undefined`

A quick way to check if the object has a property is to check if the property is defined. However, since a property could have _undefined_ as its value, it is not a reliable way to check.

Despite this disadvantage, this approach has the advantage of having the flexibility to search for properties within nested objects without throwing errors, making use of the _optional chaining operator_ `?.`.

`code`
```javascript
const user = {
  name: 'Alex',
  age: undefined,
  contact: { phone: '+123' }
}
const hasName = user.name !== undefined
const hasSurname = user.surname !== undefined
const hasAge = user.age !== undefined
const hasPhone = user.contact?.phone !== undefined
const hasAddress = user.contact?.address !== undefined
```

`test`
```javascript
test('user should have name property', () => {
 expect(hasName).toBeTruthy()
})

test('user should not have surname property', () => {
 expect(hasSurname).toBeFalsy()
})

test('user should have age property', () => {
 expect(hasAge).toBeTruthy()
})

test('user should have phone property', () => {
 expect(hasPhone).toBeTruthy()
})

test('user should not have address property', () => {
 expect(hasAddress).toBeFalsy()
})

test('user should have inherited properties', () => {
 expect(user.valueOf !== undefined).toBeTruthy()
})
```
✅ PASS user should have name property
✅ PASS user should NOT have surname property
❌ FAIL user should have age property
✅ PASS user should have phone property
✅ PASS user should NOT have address property
✅ PASS user should have inherited properties

## Using `Object.hasOwn(object1, 'prop')` method

> The Object.hasOwn() static method returns true if the specified object has the indicated property as its _own property_. If the property is inherited, or does not exist, the method returns false.

`code`
```javascript
const user = { name: 'Alex', age: undefined }
const hasName = Object.hasOwn(user, 'name')
const hasSurname = Object.hasOwn(user, 'surname')
const hasAge = Object.hasOwn(user, 'age')
```

`test`
```javascript
test('user should have name property', () => {
 expect(hasName).toBeTruthy()
})

test('user should not have surname property', () => {
 expect(hasSurname).toBeFalsy()
})

test('user should have age property', () => {
 expect(hasAge).toBeTruthy()
})

test('user should not have inherited properties', () => {
 expect(Object.hasOwn(user, 'valueOf')).toBeFalsy()
})
```
✅ PASS user should have name property
✅ PASS user should NOT have surname property
✅ PASS user should have age property
✅ PASS user should NOT have inherited properties

NOTE: Even by overwriting the _hasOwn_ method, the behavior is not affected.


## 🧐 Using `.hasOwnProperty()` method 

> Object.hasOwn() is intended as a replacement for this method.

Despite the _hasOwnProperty()_ method having better browser support, there are two important observations about the behavior:

1. hasOwnProperty() doesn't work for _null-prototype objects_, objects created like `const user = Object.create(null)`

2. It doesn't work with objects that have overridden the inherited hasOwnProperty() method.
For this second scenario there a workaround using the hasOwnProperty property from the Object prototype
`Object.prototype.hasOwnProperty.call(myObj, 'myProp')` instead of `myObj.hasOwnProperty('myProp')`
 
`code`
```javascript
// null-prototype objects
const user = Object.create(null)
user.name = 'Alex'
user.surname = 'Smith'
const hasName = user.hasOwnProperty('name')
const hasSurname = Object.prototype.hasOwnProperty.call(user, 'surname')
```

`test`
```javascript
test('user should have name property', () => {
 expect(hasName).toBeTruthy()
})

test('user should have surname using prototype', () => {
 expect(hasSurname).toBeTruthy()
})
```
❌ FAIL user should have name property
❌ FAIL user should have surname using prototype

`code`
```javascript
// hasOwnProperty overridden
const client = {
  name: 'A',
  surname: 'B',
  hasOwnProperty() { return false }
}
const hasClientName = client.hasOwnProperty('name')
const hasClientSurname = Object.prototype.hasOwnProperty.call(client, 'surname')
```

`test`
```javascript
test('client should have name property', () => {
 expect(hasClientName).toBeTruthy()
})

test('client should have surname using prototype', () => {
 expect(hasClientSurname).toBeTruthy()
})
```
❌ FAIL client should have name property
✅ PASS client should have surname using prototype