There are several strategies to check if a value is an object. The primary reason is that in JavaScript, any non-primitive type (primitives: string, number, bigint, boolean, undefined, symbol, and null) is treated as an object, but depending on the checking method, there are considerations to take into account.
Using typeof
operator
JavaScript provides the typeof operator to check the value data type.
The typeof operator returns a string indicating the type of the operand's value.
typeof variable === 'object'
returns true for:
- objects
- null 😶
- arrays
- regular expressions
- new Date()
- new Map()
- new Set()
So these validation could be required.
code
const isObject = (value) => {
return typeof value === 'object'
&& value !== null
&& !Array.isArray(value)
&& !(value instanceof RegExp)
&& !(value instanceof Date)
&& !(value instanceof Set)
&& !(value instanceof Map)
}
Using instanceof
operator
Just like typeof, multiple validations may be necessary.
variable instanceof Object
returns true for:
- functions
- arrays
- regular expressions
- new Date()
- new Map()
- new Set()
NOTE: instanceof
returns false for objects initialised as Object.create(null)
even after set additional properties.
(Object.create(null)
is used explicitly when an object without prototype chain, any properties or methods from Object.prototype is needed)
code
const user = Object.create(null)
user.id = 'A0012'
const client = Object.create(user)
const isObject = user instanceof Object
const isClientObj = client instanceof Object
test
test('user should be an object', () => {
expect(isObject).toBeTruthy()
})
test('client should be an object', () => {
expect(isClientObj).toBeTruthy()
})
❌ FAIL
user should be an object
❌ FAIL
client should be an object
💪 ✨ Object.prototype.toString.call()✨
This method returns the stringified type identifier, like [object Object]
, [object Array]
...
NOTE: toString.call()
instead of toString()
is used to prevent unexpected behaviour due this
.
The advantage of this strategy is that it distinguishes all types with specificity, such as Array, RegExp, etc. and it doesn't throw Uncaught TypeError.
code
const user = {}
const isObj = Object.prototype.toString.call(user) === '[object Object]'
test
test('user should be an object', () => {
expect(isObject).toBeTruthy()
})
✅ PASS user should be an object
The ability to return the specific type of the value allows for easy validation of other types, with the only observation that values created with a constructor and new keyword may behave differently than expected.
Ex: Object.prototype.toString.call(new Number(3))
, Object.prototype.toString.call(Number(3))
, and Object.prototype.toString.call(3)
all return '[object Number]'
.
✨ Using constructor
property
The constructor data property returns a reference to the constructor function that created the instance object
Similar to Object.prototype.toString.call()
gives the chance to know the specific type
With three important observations:
-
variable.constructor === Object
returnsfalse
forObject.create(null)
- It throws Uncaught TypeError for
null
andundefined
- Since the constructor property can potentially be overwritten, like { constructor: 1 }, it may not be reliable.
Some extra validation should be necessary (This solution does not take into account the override of constructor property):
code
const isObject = (value) => {
return value != null // equality operator checks `undefined` and `null`
&&
(value.constructor === Object || (!value.constructor && typeof value === 'object'))
}
Note: To validate other types, the only caveat is the same as that of Object.prototype.toString.call()
, concerning the type returned, whether the value is created as a primitive or using a constructor with the new keyword.
Alternative approaches for validating object values
There are also other strategies to check if a value is an object like:
-
Object.prototype.valueOf.call(value))
:
It throws Uncaught TypeError for null and undefined
-
value.--proto--
:
Deprecated
Object.getPrototypeOf(value).isPrototypeOf(Object)
Object.prototype.isPrototypeOf(value)
Top comments (0)