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.
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
const user = { name: 'Alex', age: undefined }
const hasName = 'name' in user
const hasSurname = 'surname' in user
const hasAge = 'age' in user
test
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
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
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
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
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:
hasOwnProperty() doesn't work for null-prototype objects, objects created like
const user = Object.create(null)
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 ofmyObj.hasOwnProperty('myProp')
code
// 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
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
// 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
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
Top comments (0)